Removed npm workspace configuration in favor of simple multi-project repository. Each project (web-app, housekeeping) is now completely self-contained. ## Changes - Removed root package.json and package-lock.json - Added VS Code workspace file for better project organization - Updated documentation to reflect independent project structure - Each project manages its own dependencies without workspace linking ## Structure - web-app/: Self-contained Next.js application - housekeeping/: Self-contained DB maintenance scripts - No workspace management or dependency sharing - Monorepo is purely for Git organization 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
228 lines
9.4 KiB
Markdown
228 lines
9.4 KiB
Markdown
# ToDo
|
|
* Public page with instructions
|
|
|
|
# Authentication
|
|
Authentication consists of the following parts:
|
|
* `next-auth` boilerplate
|
|
* `middleware.ts` = hooks-up `next-auth` into the page processing pipeline - user session is checked before any page is rendered
|
|
* `auth.ts` = defines how the authentication is done, and how session is checked (used by middleware)
|
|
* `/app/api/[...nextauth]/route.ts` = defines route which shows an authentication form
|
|
|
|
Source:
|
|
* [How to Implement Google Authentication in a Next.js App Using NextAuth](https://www.telerik.com/blogs/how-to-implement-google-authentication-nextjs-app-using-nextauth)
|
|
* [Next Js 14 Authentication on Edge Runtime](https://www.youtube.com/watch?v=rEopVx0FKGI)
|
|
|
|
# Multi-User Support
|
|
Each location record is marked with a user ID.
|
|
|
|
All the actions user `withUser` to fetch user ID, which is then used in all the DB operations.
|
|
|
|
# Monorepo Structure
|
|
|
|
This repository contains multiple independent projects:
|
|
|
|
- **web-app/**: Next.js application for tracking utility bills
|
|
- **housekeeping/**: Database backup and maintenance scripts
|
|
|
|
Each project is self-contained with its own dependencies and package.json.
|
|
|
|
## Working with Projects
|
|
|
|
```bash
|
|
# Web app
|
|
cd web-app
|
|
npm install
|
|
npm run dev
|
|
|
|
# Housekeeping scripts
|
|
cd housekeeping
|
|
./db-backup--standalone.sh
|
|
```
|
|
|
|
# Database Backup & Restore
|
|
|
|
The project includes multiple backup strategies for different deployment scenarios and requirements.
|
|
All backup scripts are located in the `housekeeping/` workspace.
|
|
|
|
## Backup Scripts Overview
|
|
|
|
### Standalone Docker Deployments
|
|
|
|
**Online Backups (No Downtime):**
|
|
- `housekeeping/db-dump--standalone.sh` - Creates online backup of the 'utility-bills' database using mongodump
|
|
- Database stays running during backup
|
|
- Only backs up the database content, not the full volume
|
|
- Output: `./mongo-backup/utility-bills-dump-YYYY-MM-DD_HH-MM.tar.gz`
|
|
- Default retention: 7 backups (configurable via `KEEP` env var)
|
|
- Usage: `cd housekeeping && ./db-dump--standalone.sh` or `KEEP=10 ./db-dump--standalone.sh`
|
|
|
|
- `housekeeping/db-restore-from-dump--standalone.sh` - Restores from mongodump archives
|
|
- Database stays running during restore
|
|
- **WARNING**: Drops existing collections before restore
|
|
- Usage: `cd housekeeping && ./db-restore-from-dump--standalone.sh utility-bills-dump-2025-11-26_14-30.tar.gz`
|
|
|
|
**Offline Backups (With Downtime):**
|
|
- `housekeeping/db-backup--standalone.sh` - Creates offline backup of the complete mongo-volume directory
|
|
- Database container is stopped during backup for consistency
|
|
- Backs up the entire MongoDB data directory
|
|
- Output: `./mongo-backup/mongo-volume-backup-YYYY-MM-DD-HH-MM.tar.gz`
|
|
- Default retention: 7 backups (configurable via `KEEP` env var)
|
|
- Usage: `cd housekeeping && ./db-backup--standalone.sh` or `KEEP=2 ./db-backup--standalone.sh`
|
|
|
|
### Docker Swarm Deployments
|
|
|
|
- `housekeeping/db-backup--swarm.sh` - Creates offline backup by scaling down the MongoDB service
|
|
- Service is scaled to 0 during backup
|
|
- Output: `./mongo-backup/mongo-volume-backup-YYYY-MM-DD-HH-MM.tar.gz`
|
|
- Usage: `cd housekeeping && ./db-backup--swarm.sh`
|
|
|
|
- `housekeeping/db-restore-from-backup--swarm.sh` - Restores volume backup by scaling down the service
|
|
- Service is scaled to 0 during restore
|
|
- Optional `--pre-backup` flag for safety backup before restore
|
|
- Usage: `cd housekeeping && ./db-restore-from-backup--swarm.sh mongo-volume-backup-2025-11-26-14-30.tar.gz`
|
|
|
|
## Automated Backup Schedule
|
|
|
|
Backups run automatically via cron at 04:00 every day:
|
|
|
|
```cron
|
|
# Sunday: Full volume backup (offline), keep 2 backups
|
|
0 4 * * 0 cd /home/knee-cola/web-pro/evidencija-rezija/housekeeping && KEEP=2 ./db-backup--standalone.sh
|
|
|
|
# Monday-Saturday: Database dump (online), keep 6 backups
|
|
0 4 * * 1-6 cd /home/knee-cola/web-pro/evidencija-rezija/housekeeping && KEEP=6 ./db-dump--standalone.sh
|
|
```
|
|
|
|
**Backup Strategy:**
|
|
- **Sundays**: Full volume backup with brief downtime (keeps 2 full backups)
|
|
- **Monday-Saturday**: Online database dumps without downtime (keeps 6 daily backups)
|
|
- **Safety net**: The retention policy ensures that at any point you have 2 full backups plus the 6 daily dumps created between them. This provides up to 2 weeks to detect and recover from a corrupted or inconsistent full backup before losing the previous full backup and its associated daily dumps.
|
|
|
|
All backup operations are logged with timestamps:
|
|
- Volume backups: `./mongo-backup/db-backup-standalone.log`
|
|
- Database dumps: `./mongo-backup/db-dump-db-standalone.log`
|
|
- Restore operations: `./mongo-backup/db-restore-from-dump.log`
|
|
|
|
## Backup Directory
|
|
|
|
All backups are stored in `./mongo-backup/`:
|
|
- Volume backups: `mongo-volume-backup-YYYY-MM-DD-HH-MM.tar.gz` (full mongo-volume directory)
|
|
- Database dumps: `utility-bills-dump-YYYY-MM-DD_HH-MM.tar.gz` (utility-bills database only)
|
|
|
|
This directory is excluded from git via `.gitignore`.
|
|
|
|
# Deploying
|
|
The deployment is done via Docker:
|
|
* build docker image
|
|
* deploy Docker service
|
|
|
|
## Building Docker image
|
|
Run the following command:
|
|
```bash
|
|
build.sh
|
|
```
|
|
The image will be stored in the local Docker instance.
|
|
|
|
## Deploying Docker service
|
|
Run the following command:
|
|
```bash
|
|
deploy.sh
|
|
```
|
|
|
|
# Implementation details
|
|
## Issues with HOSTNAME
|
|
When deplyed via docker and published via Cloudflare there's an issue with `HOSTNAME` env variable:
|
|
* if left unset, the server will use IP address assigned to container by Docker (i.e. 10.0.20.3) and **will not accept connections from outside**
|
|
```
|
|
▲ Next.js 14.0.2
|
|
- Local: http://68db6c9ebafe:80
|
|
- Network: http://10.0.20.3:80
|
|
```
|
|
* if set to "0.0.0.0" the server will serve static pages, but will *reject API calls when submitting form*
|
|
```
|
|
▲ Next.js 14.0.2
|
|
- Local: http://localhost:80
|
|
- Network: http://0.0.0.0:80
|
|
|
|
utility-bills-tracker_web-app.1.Error: Invalid Server Actions request.
|
|
|
|
`x-forwarded-host` header with value `0.0.0.0:80` does not match `origin` header with value `rezije.app` from a forwarded Server Actions request. Aborting the action.
|
|
```
|
|
* if set to "rezije.app" the server will not start since the IP address it resolves with the given FQDN does not match any of the IP addresses assigned to the container
|
|
```
|
|
▲ Next.js 14.0.2
|
|
- Local: http://localhost:80
|
|
- Network: http://0.0.0.0:80
|
|
|
|
utility-bills-tracker_web-app.1.Error: Invalid Server Actions request.
|
|
|
|
`x-forwarded-host` header with value `rezije.app:80` does not match `origin` header with value `rezije.app` from a forwarded Server Actions request. Aborting the action.
|
|
```
|
|
|
|
So there are the following issues:
|
|
* server will not accept external request - can be fixed by setting `HOSTNAME` to `0.0.0.0`
|
|
* server rejects API requests - can be fixed by adding `serverActions.allowedOrigins` option to `nextjs.config.js` file
|
|
|
|
So these are the fixes which were implemented in order to be able to run server in production.
|
|
|
|
This is a hack indicating that I don't understand how the damn thing should be configured!
|
|
|
|
Even when this hack is emplyed the server still logs the followig error:
|
|
```
|
|
failed to get redirect response TypeError: fetch failed
|
|
at Object.fetch (node:internal/deps/undici/undici:11730:11)
|
|
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
|
|
cause: Error: connect ECONNREFUSED 0.0.0.0:443
|
|
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1555:16)
|
|
at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:128:17) {
|
|
errno: -111,
|
|
code: 'ECONNREFUSED',
|
|
syscall: 'connect',
|
|
address: '0.0.0.0',
|
|
port: 443
|
|
}
|
|
}
|
|
```
|
|
|
|
## Mongo DB & AVX Instructions
|
|
The MongoDB server v > 5.0 will not run on and old machine such as Acer Revo due to it's CPU.
|
|
|
|
This issue was solved by using an older Mongo DB Version 4.4.27
|
|
|
|
## Decoding Barcode
|
|
|
|
Barcode decoding is slow and can lead to locking of the main thread.
|
|
This heavy lifting could be moved to background thread.
|
|
|
|
The new solution could be based on the following code: https://github.com/pocesar/react-use-qrcode
|
|
That solution uses video to decode the QR code. This could be modified so that it uses data stored in canvas.
|
|
|
|
# OAuth verification video transcript
|
|
|
|
This is Rezije app demonstration for Google OAuth login process for the users of our app.
|
|
The video shows that our usage of OAuth scopes complies with Google API services user data policy.
|
|
|
|
Our app does not use any of the sensitive and restricted scopes.
|
|
|
|
User's can access Privacy policy and Terms of service by clicking links in footer of the page.
|
|
|
|
Let's first click on the "Privacy policy" link. Here we can see our Privacy policy.
|
|
|
|
Now let's click on the "Terms of service" link. Here We can see our Terms of service.
|
|
|
|
Links to these pages are assigned in the apropriate fields in the Google's Cloud Console and are listed in the Google's OAuth window.
|
|
|
|
From our homepage you can login by clicking on the "Sign in with Google" button.
|
|
|
|
Here in the address bar you can see that the client ID is present in the URL.
|
|
|
|
Select the account with which you would like to sign-in with and you will be logged in into the app.
|
|
|
|
We are using two scopes:
|
|
1. OAuth ID, which is used to assign ownership to the application specific data when it's is stored and retrieved from our database.
|
|
2. user's e-mail address, which is stored in our database so that we can contact our users in case such action is needed.
|
|
|
|
For any questions regarding the use of the Google API service please feel free to reach out at support@rezije.app
|
|
|
|
# Localization
|
|
Localization was done by following video: https://www.youtube.com/watch?v=uZQ5d2bRMO4 |