Professional Database Management System with Vue.js Frontend and FastAPI Backend.
- ✅ User Authentication with JWT tokens
- ✅ Two-Factor Authentication (OTP)
- ✅ OTP Reset via Email (User + Support initiated)
- ✅ Role-Based Access Control (RBAC)
- ✅ User Registration with Email Verification
- ✅ Password Reset Functionality
- ✅ Email Change with Verification
- ✅ User Profile Management
- ✅ Support Team User Management
- ✅ Record Management (CRUD operations)
- ✅ Keyword Management for Names and Locations
- ✅ Phonetic Search (Cologne Phonetic & Double Metaphone)
- ✅ Pages Management with PDF Upload
- ✅ PDF Watermarking with User Information (a)
- ✅ Dynamic Thumbnail Generation with Watermarks (a)
- ✅ File Upload to Filesystem
- ✅ Restriction & WorkStatus Management
- ✅ Full-text Search and Filtering
- ✅ Multi-language Support (English, German)
- ✅ PostgreSQL Database with SQLAlchemy ORM
- ✅ Alembic migrations support for schema changes
- ✅ RESTful API with FastAPI
- ✅ Vue.js 3 Frontend with Pinia Store
- ✅ Dynamic Logo and Favicon Loading from Backend
- ✅ External Legal HTML Content (Imprint, Data Protection, Terms of Service per language)
- ✅ Environment-based Configuration (Dev/Production)
- ✅ Comprehensive Unit Tests
- Python 3.9+
- Node.js 16+
- PostgreSQL 12+
- PyMuPDF runtime support (installed via
requirements.txt) (a)
cd backend
python -m venv venv
source venv/bin/activate # or: venv\Scripts\activate (Windows)
pip install -r requirements.txt
# Configure .env file (copy from .env.example)
cp .env.example .env
# Initialize database
python scripts/init_db.py
# Start development server
python -m app.mainBackend runs at: http://localhost:8000
cd frontend
npm install
# Configure .env.local file (copy from .env.example)
cp .env.example .env.local
# Start development server
npm run devFrontend runs at: http://localhost:3000
- Installation & Setup Guide
- API Documentation (Swagger UI)
- API ReDoc
Legal pages:
- Imprint and Data Protection are available in the frontend information area.
- Terms of Service remains available at
/terms-of-serviceand via registration / Swagger links, but is intentionally not shown in the navigation.
- Start backend and open:
http://localhost:8000/docs - Base API prefix is:
/api/v1
- Open endpoint:
POST /api/v1/auth/login - Click
Try it out - Enter JSON body:
{
"username": "your_username",
"password": "your_password",
"otp_code": "123456"
}Notes:
otp_codeis required when OTP is enabled for the user.otp_codeis also required for users with rolesupportoradmin.- On success, response contains
access_tokenandtoken_type. - Login lock behavior after failed attempts is configured via backend
.env:GRACE_PERIOD_MINUTES_3_ATTEMPTSapplies from 3 failed logins.GRACE_PERIOD_MINUTES_5_ATTEMPTSapplies from 5 failed logins.- During active grace period, backend returns:
Login temporarily locked. Please try again later.
- Username rules for
POST /api/v1/auth/register:- Minimum length is 5 characters (after trimming).
- Leading and trailing spaces are removed automatically.
- Duplicate usernames are rejected with a clear validation error.
- Closed registration behavior:
- If
features.closedRegistration=true, public registration is hidden in frontend login/navigation. - New registrations are then only possible for authenticated
supportoradminusers.
- If
- Click
Authorize(top-right). - Paste the token from
access_tokeninto the bearer auth field. - If needed, use format:
Bearer <access_token>. - Click
Authorize, thenClose.
- Example:
GET /api/v1/users/profile - Click
Try it out->Execute - If authentication is correct, you get the user profile response.
401 Unauthorized: token missing/invalid/expired.401 Unauthorized: login failed (e.g. wrong credentials, missing OTP, invalid OTP, inactive account).
nlf/db/
├── backend/ # Python FastAPI backend
│ ├── app/ # Main application
│ ├── scripts/ # Database initialization
│ ├── tests/ # Backend unit tests
│ └── config.py # Configuration
│
├── frontend/ # Vue.js frontend
│ ├── src/ # Source code
│ ├── tests/ # Frontend unit tests
│ └── vite.config.js
│
└── install.md # Setup guide
cd backend
pytest tests/ -v --cov=appcd frontend
npm run testThis repository includes a GitHub Actions workflow at .github/workflows/pull-request-tests.yml, which automatically runs on every pull request:
- Backend unit tests with
pytest - Frontend unit tests with
vitest
The results are published as checks on the pull request.
To block merging when tests fail, you also need to configure a branch protection rule in GitHub for the target branch:
- Open the repository
Settings->Branches- Create or edit a branch protection rule for the target branch (for example,
main) - Enable
Require status checks to pass before merging - Mark the checks
Backend TestsandFrontend Testsas required
GitHub will allow merging only after both checks pass.
CREATE USER nlf_user WITH PASSWORD 'nlf_password';
CREATE DATABASE nlf_db OWNER nlf_user;
CREATE DATABASE nlf_db_test OWNER nlf_user;- User - User accounts with authentication
- Role - User roles (admin, support, user)
- Permission - Granular permissions
- UserRole - User-to-role mapping
- RolePermission - Role-to-permission mapping
- UserRegistration - Registration tokens
- PasswordResetToken - Password reset tokens
- Record - Main data records with title, signature, description
- Page - Pages within records with PDF file support
- KeywordName - Keywords for names (with phonetic search)
- KeywordLocation - Keywords for locations (with phonetic search)
- RecordsKeywordsName - Record-to-name-keyword mapping
- RecordsKeywordsLocation - Record-to-location-keyword mapping
- Restriction - Access restrictions (none, confidential, locked, privacy)
- RestrictionDetail - Detailed restriction information
- WorkStatus - Workflow status tracking
- WorkStatusArea - Status area categories (record, page)
POST /api/v1/auth/register- Register new user. Username is trimmed (leading/trailing spaces removed), must be at least 5 characters, and must be unique acrossusersand pendinguser_registrations. WhenCLOSED_REGISTRATION=trueand at least one user already exists, this endpoint is limited to authenticatedsupportoradminusers. The bootstrap case stays open until the first user exists.POST /api/v1/auth/login- Login userPOST /api/v1/auth/password-reset- Request password resetPOST /api/v1/auth/password-reset/confirm/{token}- Confirm password resetGET /api/v1/auth/otp-reset/confirm/{token}- Validate support OTP reset link and return temporary OTP setup payloadPOST /api/v1/auth/otp-reset/confirm/{token}- Confirm OTP reset with one OTP code
GET /api/v1/users/profile- Get user profilePUT /api/v1/users/profile- Update profilePOST /api/v1/users/password-change- Change passwordPOST /api/v1/users/email-change- Change email
GET /api/v1/users- List users (with filtering and pagination)GET /api/v1/users/{id}- Get user detailsPUT /api/v1/users/{id}- Update userPUT /api/v1/users/{id}/password-reset- Trigger password reset email (support/admin)PUT /api/v1/users/{id}/otp-reset- Trigger OTP reset email (support/admin, 24h expiry by default)
Notes:
- User list/detail responses include
otp_enabledso support can see whether OTP is configured. - Support-triggered OTP reset stores link tokens in
otp_reset_tokensand follows the same confirmation flow as user OTP reset.
GET /api/v1/records- List records (with search and pagination)GET /api/v1/records/{id}- Get record detailsPOST /api/v1/records- Create new recordPUT /api/v1/records/{id}- Update recordDELETE /api/v1/records/{id}- Delete record (soft delete)GET /api/v1/records/restrictions- Get available restrictionsGET /api/v1/records/workstatus- Get available work statuses
GET /api/v1/pages- List pages (filterable by record_id)GET /api/v1/pages/{id}- Get page detailsPOST /api/v1/pages- Create new page with PDF upload; multi-page PDFs are split into separate page entriesPUT /api/v1/pages/{id}- Update page (with optional file upload)DELETE /api/v1/pages/{id}- Delete page (soft delete)GET /api/v1/pages/{id}/view-pdf- View PDF with user-specific watermark (inline)GET /api/v1/pages/{id}/thumbnail?width=200- Get thumbnail with watermarkGET /api/v1/pages/{id}/download-watermarked- Download PDF with watermarkGET /uploads/{signature_folder}/{filename}- (Deprecated) Direct file access - use watermarked endpoints
GET /api/v1/config- Get public application configuration, includingfeatures.closedRegistrationandfeatures.closedRegistrationConfigured
ENVIRONMENT=development
DB_HOST=localhost
DB_PORT=5432
DB_USER=nlf_user
DB_PASSWORD=nlf_password
DB_NAME=nlf_db
SECRET_KEY=your-secret-key
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password
USER_OTP_RESET_TOKEN_EXPIRE_HOURS=1
SUPPORT_OTP_RESET_TOKEN_EXPIRE_HOURS=24
# Application Configuration
APP_NAME=NLF Database
COMPANY_NAME=Your Company
LOGO_URL=/assets/logo.png
# Feature Flags
FEATURE_OTP_ENABLED=true
FEATURE_EMAIL_VERIFICATION_ENABLED=true
FEATURE_CORPORATE_APPROVALS_ENABLED=true
CLOSED_REGISTRATION=false
# File Upload Configuration
UPLOAD_DIRECTORY=./uploads
MAX_UPLOAD_SIZE=52428800
# Logo & Watermark Configuration
# Logo URL is served via /assets static mount
WATERMARK_IMAGE_PATH=./assets/logo.png
# Optional logo embedded inside record QR codes (72x72 px)
# Leave empty to generate QR codes without logo
QR_CODE_LOGO_PATH=./assets/Logo_NLF_fregestellt_75x75.pngMAX_UPLOAD_SIZE is configurable via .env and is interpreted in bytes. The default value 52428800 equals 50 MB.
PDF storage behavior:
- The base storage directory is controlled by
UPLOAD_DIRECTORY. - The subfolder name is built from the trimmed record signature with whitespace replaced by
_. - Single-page PDFs are stored as
Seite_yyyyMMdd_hhmmss.pdf. - Multi-page PDFs are split into separate page entries and stored as
Seite_1.pdf,Seite_2.pdf, etc.
OCR processing logs:
- The backend logs OCR runtime per file and per job.
- After an upload batch is processed, a summary log shows processed file count and total duration.
- Example:
Upload batch OCR complete. files=5 duration=312.4s record_id=6f9d...
# Backend API Base URL (without /api/v1 suffix)
VITE_BACKEND_URL=http://localhost:8000
# API URL (full path to API endpoints)
VITE_API_URL=http://localhost:8000/api/v1# Production backend URL
VITE_BACKEND_URL=https://api.yourdomain.com
VITE_API_URL=https://api.yourdomain.com/api/v1Important Notes:
LOGO_URLis a URL path (e.g.,/assets/logo.png), served via backend static mountWATERMARK_IMAGE_PATHis a file system path (e.g.,./assets/logo.png), relative to backend directoryQR_CODE_LOGO_PATHis a file system path for record QR codes (rendered centered at 72x72 px); if not set, QR codes are generated without logo- Frontend automatically fetches logo URL from backend config endpoint
- Favicons are loaded dynamically from
/assets/favicons/usingVITE_BACKEND_URL
- Create feature branch:
git checkout -b feature/your-feature - Make your changes
- Write/update tests
- Commit:
git commit -am 'Add feature' - Push:
git push origin feature/your-feature - Create Pull Request
- Backend: Python with Black, Flake8, isort
- Frontend: JavaScript/Vue with ESLint
# Backend
cd backend
black app tests
flake8 app tests
# Frontend
cd frontend
npm run lint- Change SECRET_KEY in production
- Use environment variables for sensitive data
- Enable HTTPS in production
- Implement rate limiting
- Regular dependency updates
GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later) - See LICENSE for details.
For issues or questions:
- Check the Installation Guide
- Review API Documentation
- Create an issue in the project Git repository (to be published)
Your Company
Version: 1.0.0
Last Updated: 2026-03-23