Feedback Flow is an application designed to manage the feedback from product testers.
This application is intended for product testers who wish to test a product and provide their opinion on it. The management of products offered by sellers is outside the scope of this application.
The application allows the tester to manage their purchases, feedback, and refunds.
Thus, in case of a dispute with a seller, the tester can prove that they have indeed purchased the product and provided their opinion on it, and that this opinion has been published by the platform.
The application is based on the Vite, Auth0 & HeroUI Template, which is a starter template for a React 19 application with Auth0 and HeroUI 2.7, along with a backend running on a Worker C.
The application is deployed on a CDN as a static SPA (Single Page Application). It communicates with a REST API deployed on a free Cloudflare worker.
- A product tester is someone who tests a product and provides feedback on it.
- A product is an item or service that is tested by a tester.
- The seller is the person who sells the product.
- Feedback is the opinion given by the tester about the product.
The testing and feedback process is as follows:
- The seller offers a product to a tester.
- The tester purchases the product.
- The tester provides the seller with a screenshot of the proof of purchase.
- The tester tests the product.
- The tester gives their opinion on the product.
- The feedback is published on the feedback platform.
- The tester provides a screenshot of the published feedback.
- The seller refunds the tester.
There may be a delay between the purchase and the refund. Additionally, the refund amount may differ from the purchase amount. Therefore, the following information should be stored:
- Tester's name
- Tester's ID (a single tester can have multiple IDs)
- Purchase date
- Order number
- Purchase amount
- Screenshot of the proof of purchase
- Date feedback was submitted
- Date feedback was published
- Screenshot of the published feedback
- Date feedback was sent to the seller
- Refund date
- Refund amount
permission | description |
---|---|
admin:api | Administer the API |
read:api | Read one's own feedback data |
write:api | Write one's own feedback data |
backup:api | Manage the database |
- Add a tester (admin:api)
- Add an ID to a tester (admin:api)
- Add a product to test (write:api)
- Add a purchase (write:api)
- Add feedback (write:api)
- Publish feedback (write:api)
- Send feedback to the seller (write:api)
- Record a refund (write:api)
- View non-refunded feedbacks (read:api)
- View refunded feedbacks (read:api)
Authentication is handled by Auth0. The system is provided by the template. It is an OAuth 2.0 process that runs in the browser. The browser receives a JWT token, which it sends to the API. The API verifies the token and grants or denies access to the resource based on the permissions included in the token.
A Swagger ui is automatically generated for the API. It is available at /docs
. The API is secured with Auth0. The API uses the same authentication process as the application. The API requires a JWT token to be sent in the Authorization
header of each request. The token must be prefixed with Bearer
.
For easily adding the token, simply click on your name in the footer of the application. A modal will open with the token. Copy the token with the supplied button and paste it in the Authorization
field in Swagger. The token is valid for 24 hours. The API uses the same permissions as the application. The API uses the same database as the application.
The REST API exchanges all objects in JSON format. The API provides the following endpoints:
-
GET
/api/testers
- Retrieve all testers with pagination - requires admin:api permission- Optional parameters:
?page=1&limit=10&sort=name&order=asc
- Response:
{success: boolean, data: [{uuid: string, name: string, ids: string[]}], total: number, page: number, limit: number}
- Optional parameters:
-
POST
/api/tester
- Add a tester to the database (their ID is automatically generated with a UUID) - requires admin:api permission- Request:
{name: string, ids: string[]|string}
- Response:
{success: boolean, uuid: string}
- Request:
-
POST
/api/tester/ids
- Add an ID to the authenticated tester - requires admin:api permission- Request:
{name: string, id: string}
- Response:
{success: boolean, name: string, ids: [string]}
- Request:
-
GET
/api/tester
- Get information about the authenticated tester - requires admin:api permission- Response:
{success: boolean, data: {uuid: string, name: string, ids: [string]}}
- Response:
-
POST
/api/purchase
- Add a purchase to the database - requires write:api permission- Request:
{date: string, order: string, description: string, amount: number, screenshot: string}
- Response:
{success: boolean, id: string}
- Request:
-
GET
/api/purchase/:id
- Get information about a specific purchase - requires read:api permission- Response:
{success: boolean, data: {id: string, date: string, order: string, description: string, amount: number, screenshot: string}}
- Response:
-
DELETE
/api/purchase/:purchaseId
- Delete a purchase by ID - requires write:api permission- Response:
{success: boolean, message: string}
- Response:
-
GET
/api/purchase
- Get a list of the authenticated tester's purchases - requires read:api permission- Optional parameters:
?page=1&limit=10&sort=date&order=desc
- Response:
{success: boolean, data: [{id: string, date: string, order: string, description: string, amount: number}], total: number, page: number, limit: number}
- Optional parameters:
-
GET
/api/purchases/not-refunded
- Get a list of the authenticated tester's not-refunded purchases - requires read:api permission- Optional parameters:
?page=1&limit=10&sort=date&order=desc
- Response:
{success: boolean, data: [{id: string, date: string, order: string, description: string, amount: number}], total: number, page: number, limit: number}
- Optional parameters:
-
GET
/api/purchases/refunded
- Get a list of the authenticated tester's refunded purchases - requires read:api permission- Optional parameters:
?page=1&limit=10&sort=date&order=desc
- Response:
{success: boolean, data: [{id: string, date: string, order: string, description: string, amount: number}], total: number, page: number, limit: number}
- Optional parameters:
-
GET
/api/purchase-status
- Get the status of all purchases with feedback/publication/refund status - requires read:api permission- Optional parameters:
?page=1&limit=10&sort=date&order=desc&limitToNotRefunded=false
- Response:
{success: boolean, data: [{id: string, date: string, order: string, description: string, amount: number, refunded: boolean, has_feedback: boolean, has_publication: boolean, has_refund: boolean}], total: number, page: number, limit: number}
- Optional parameters:
-
GET
/api/purchases/refunded-amount
- Get total amount of refunded purchases - requires read:api permission- Response:
{success: boolean, amount: number}
- Response:
-
GET
/api/purchases/not-refunded-amount
- Get total amount of non-refunded purchases - requires read:api permission- Response:
{success: boolean, amount: number}
- Response:
-
POST
/api/feedback
- Add feedback to the database - requires write:api permission- Request:
{date: string, purchase: string, feedback: string}
- Response:
{success: boolean, id: string}
- Request:
-
GET
/api/feedback/:id
- Get information about specific feedback - requires read:api permission- Response:
{success: boolean, data: {date: string, purchase: string, feedback: string}}
- Response:
-
POST
/api/publish
- Record the publication of feedback - requires write:api permission- Request:
{date: string, purchase: string, screenshot: string}
- Response:
{success: boolean, id: string}
- Request:
-
GET
/api/publish/:id
- Get information about a specific publication - requires read:api permission- Response:
{success: boolean, data: {date: string, purchase: string, screenshot: string}}
- Response:
-
POST
/api/refund
- Record a refund - requires write:api permission- Request:
{date: string, purchase: string, refundDate: string, amount: number, transactionId?: string}
- Response:
{success: boolean, id: string}
- Request:
-
GET
/api/refund/:id
- Get information about a specific refund - requires read:api permission- Response:
{success: boolean, data: {date: string, purchase: string, refundDate: string, amount: number, transactionId?: string}}
- Response:
-
GET
/api/backup/json
- Backup the database - requires backup:api permission- Response:
{success: boolean, data: {backup: string}}
- Response:
-
POST
/api/backup/json
- Restore the database - requires backup:api permission- Request:
{backup: string}
- Response:
{success: boolean}
- Request:
-
GET
/api/__d1/schema
- Get database table names - requires admin:api permission- Response:
{tables: string[], timestamp: string}
- Response:
-
GET
/api/__d1/schema_version
- Get database schema version - requires admin:api permission- Response:
{version: {version: number, description: string}, timestamp: string}
- Response:
-
GET
/api/__d1/schema_migrations
- Execute database schema migrations - requires admin:api permission- Response:
{migrations: string[], timestamp: string}
- Response:
The application is developed using React 19, Vite, and Tailwind CSS. The backend is developed using Cloudflare Workers and the Cloudflare D1 database.
The repository is structured as a monorepo with the following structure:
Clone the repository and install the dependencies:
git clone https://github.com/sctg-development/feedback-flow.git
cd feedback-flow/cloudflare-worker
npm ci
cd ../client
npm ci
See the Auth0.md file for detailed instructions on how to configure Auth0 for the application.
The application requires the following environment variables to be set in a .env
file in the root of the repository:
AUTH0_CLIENT_ID=your_auth0_client_id
AUTH0_CLIENT_SECRET=your_auth0_client_secret
AUTH0_DOMAIN=your_auth0_domain
AUTH0_SCOPE="openid profile email read:api write:api admin:api backup:api"
AUTH0_AUDIENCE="http://localhost:8787/api"
AUTH0_SUB=your_current_user_token_sub
API_BASE_URL=http://localhost:8787/api
CORS_ORIGIN=http://localhost:5173
READ_PERMISSION=read:api
WRITE_PERMISSION=write:api
ADMIN_PERMISSION=admin:api
BACKUP_PERMISSION=backup:api
CRYPTOKEN=any_random_string_to_encrypt_the_variables_in_the_repo
AMAZON_BASE_URL="https://www.amazon.fr/gp/your-account/order-details?orderID="
DB_BACKEND=memory # or d1
DB_MAX_IMAGE_SIZE=640
AUTH0_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJnaXRodWJ8MTIzNDU2Nzg5MCIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMiwicGVybWlzc2lvbnMiOiJyZWFkOmFwaSB3cml0ZTphcGkgYWRtaW46YXBpIGJhY2t1cDphcGkifQ.m1URdlBbuHa9_e3xN2MEMnkGm3ISbVBAuW7fWgL7fms"
To run the application, you need to start both the client and the server. In the root of the repository, run the following command:
-
Start the development server backend:
cd cloudflare-worker && npm run dev:env
-
Start the development server frontend in another terminal:
cd client && npm run dev:env
-
The application should now be running at http://localhost:5173 and the API at http://localhost:8787.
-
Connect to the application with your browser to http://localhost:5173.
-
Log in to the application with your GitHub account. Copy the token from the application, you can find it by clicking on your name in the appliation footer.
-
Copy the token and paste it in the
AUTH0_TOKEN
variable in the.env
file. -
Restart the Cloudflare Worker.
-
in the
cloudflare-worker
folder, run the following command test the worker and add some data to the TESTER user linked to your GitHub account:cd cloudflare-worker npm test
Generate a PDF report for purchases ready to refund
Add a new user (Admin menu, dark mode)