Running a shop means trusting your neighbors, but keeping track of 'deni' shouldn't be a headache. My app is a digital ledger that sits right in your pocket. You can register your regular customers, record what they take on credit, and see exactly when they pay you back. If you decide to forgive a small debt, the app handles that too. It’s built to make sure you get paid on time and your records are always organized
- This project requires that docker desktop and docker-compose be installed on your computer.
!! IMPORTANT !!
- Make a file named
.envin the root folder of the projects and populate it with the following values (this is not standard practice but it is easy for such test projects): MYSQL_DATABASE=pesapaldb MYSQL_ROOT_PASSWORD=example MYSQL_ROOT_USER=root MYSQL_HOST=db
- Make sure docker desktop is running.
- Initialize docker services and the database, use always use command
docker-compose down -v && docker compose build --no-cache && docker compose up. This command clears old database, rebuilds without cache and restarts the application with fresh instance of the database. - To access the database via the shell, execute the following command in a fresh terminal window:
docker exec -it pesapal-db-container bash- Once in the shell of the container, login using command
mysql -u root -p - When prompted for password, enter the value assigned to
MYSQL_ROOT_PASSWORDenvironment variable in file '.env' - Create a database using command
CREATE DATABASE pesapaldb; - Select the newly created database using command
USE pesapaldb; - The database commands can be found in
docker-entrypoint-initdb.d/init.sql
- Once in the shell of the container, login using command
- Server url: localhost:3003/
- The endpoints of the server can be tested independently via postman.
- The whole set of available endpoints is published here
- Alternatively, open
./server/server.jsto see the complete set of endpoints
- The API server now reads
PORTand defaults to3003. - The API server now reads database settings from env and supports both
MYSQL_USER/MYSQL_PASSWORDand the previousMYSQL_ROOT_USER/MYSQL_ROOT_PASSWORD. - The web app defaults to calling
/apifrom the browser instead of hardcodinghostname:3003. - For local Vite development outside Docker,
/apiis proxied tohttp://127.0.0.1:3003. - For Docker development, the
webservice setsVITE_API_PROXY_TARGET=http://server:3003. - For production with Docker, the
webcontainer serves the built static app with Nginx and proxies/api/*to theservercontainer. - For production on a Linux server with an external reverse proxy, point the proxy at the
webcontainer rather than Vite. Example with Nginx:
location /api/ {
proxy_pass http://127.0.0.1:3003/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}- If you do not want to use a reverse proxy path, set
VITE_API_BASE_URLat build time to your public API URL.
- Use
compose.yamlfor local development only. - Use
compose.prod.yamlfor server deployment. - The production web image no longer runs Vite. It builds the React app and serves the generated
distfiles from Nginx. - The production server image no longer runs
nodemon. It runsnpm run start. - The service worker is disabled for now to avoid stale deploys being cached by browsers.
- Production MySQL data lives in the Docker named volume
mysql-data. That protects data from container restarts, but it is not a backup.
- Copy the repo to the server.
- Create or update
.envwith production secrets. - Build and start the production stack:
docker compose -f compose.prod.yaml up -d --build - Verify locally on the server:
curl http://127.0.0.1:8080 - Point your existing gateway / Nginx proxy to
http://127.0.0.1:8080.
- If your Contabo server already has Nginx, a gateway, or another reverse proxy running, do not expose Vite or the Node API publicly.
- Route your public domain to the
webcontainer only. - The
webcontainer already proxies/api/*internally to theservercontainer. - If your gateway prefers Docker networks instead of host ports, you can remove
8080:80fromcompose.prod.yamland attach both stacks to the same external Docker network.
- Pull the latest code on the server.
- Rebuild and restart:
docker compose -f compose.prod.yaml up -d --build - Confirm health:
docker compose -f compose.prod.yaml ps
- Use
./scripts/backup-db.shto create a compressed MySQL dump on the host machine outside the repo by default, at~/kitabu-backups/mysql/. - The script reads
.env, connects to the runningdbservice fromcompose.prod.yaml, writes a timestamped.sql.gzfile, and deletes backups older than 14 days by default. - Make the script executable once:
chmod +x ./scripts/backup-db.sh - Run it manually:
./scripts/backup-db.sh - Keep backups longer by overriding retention:
RETENTION_DAYS=30 ./scripts/backup-db.sh - Store backups in a different folder if you want them on another disk:
BACKUP_DIR=/srv/kitabu-backups ./scripts/backup-db.sh
Use the server's crontab so backups happen even when you forget:
0 */6 * * * cd /path/to/pesapal && /bin/bash ./scripts/backup-db.sh >> /var/log/kitabu-backup.log 2>&1- The example above runs every 6 hours.
- A good minimum for real customers is every night; every 6 hours is safer.
- For stronger protection, sync the generated backup folder to a second machine or cloud bucket as a separate step.
The visual web application can be accessed via link: http://localhost:5173/
Please note that due to limited time, some areas of the app especially on the front-end were done in a hurry and thus the quality may not be as great.