A web-based system to create, manage, and monitor agreements within an organization. The application is built with:
Frontend: React (Vite)
Backend: Django (REST Framework)
Database: MySQL
The project provides role-based access control with Superusers, Regular Users, and Executives, ensuring secure management of agreements, departments, and permissions.
-
User Authentication
- Superuser creates regular users.
- Regular users log in using email + password.
-
Dashboard
- Interactive graphs and charts displaying agreements:
- By department
- By expiry dates (e.g., 6, 4, 3, 2 months)
- Interactive graphs and charts displaying agreements:
-
Agreement Management
- Regular users can create agreements (restricted to assigned departments).
- Agreement lifecycle: Preview โ Submit โ View/Edit.
-
Executivesโ Access
- Executives can only view agreements (no create/edit permissions).
-
Administration Panel (Accessible only by Superuser)
- Create and manage departments
- Create users (mark as active/inactive)
- Assign department permissions
- Add vendors and agreement types
- Manage agreements and permissions
- Only a superuser can delete agreements
-
User Roles
- Superuser = User + Staff Status + Superuser Status
- Regular User = Assigned to departments with specific permissions
- Executive = Can approve permissions, but cannot edit agreements
-
Permission Flow
- Create Department โ Create User โ Assign Department Permission โ Add Vendor โ Create Agreement
- To give extra department access: Department โ User โ Department Permission
- Permissions require Executive approval
- Deactivating a user (
Active = false) removes system access
| Role | Permissions |
|---|---|
| Superuser | Full access โ create departments, users, vendors, agreement types, delete agreements, assign permissions. |
| Regular User | Login โ view dashboard, create/preview/edit agreements in assigned department. |
| Executive | View agreements only, approve department permissions. |
Stack
- Frontend: React (Vite)
- Backend: Django (Gunicorn)
- Database: MySQL
- Web Server/Reverse Proxy: Nginx
- OS: RHEL 8.10
Important
This section is a project deployment guide step by step
- RHEL 8.10 server with sudo access
- Domain or server IP (e.g.,
<SERVER_IP>) reachable over port - Git + SSH access to your repository
Optional but helpful:
treefor directory inspection,firewalldenabled
sudo dnf install -y epel-release
sudo dnf update -y
sudo dnf install -y python3 python3-pip python3-virtualenv mysql-server nginx nodejs npm tree
sudo systemctl enable --now mysqld
sudo systemctl enable --now nginxTip
If mysqlclient build fails during pip install, add build tools:
sudo dnf install -y gcc gcc-c++ make python3-devel mariadb-connector-c-devel pkg-config
- Donโt commit
.env, private keys, or real passwords. - Use strong DB/user passwords via env vars or a secrets manager.
- In Django, set
DEBUG=Falseand configureALLOWED_HOSTSfor<SERVER_IP>or your domain.
# settings.py
DEBUG = False
ALLOWED_HOSTS = ["<SERVER_IP>", "your.domain.com"]<PROJECT_ROOT>/
โโ atm/ # Django backend project
โโ Atm_fproject/ # React (Vite) frontend
โโ venv/ # Python virtualenv (created later)
โโ docs/ # Optional: diagrams & documentation
Note
In the examples below we use <PROJECT_ROOT> = /app01/atm. Adjust as needed.
sudo mkdir -p /app01
sudo chown -R $USER:$USER /app01
cd /app01
# Example (replace with your repo + branch)
# ssh key should already be added to your Git hosting account
# ssh-ed25519 AAAA... <your_email> (do NOT publish private keys)
git clone --branch <BRANCH_NAME> <REPO_SSH_URL> atm
cd /app01/atm
ls -la
git branch
# Optional: verify all files
cd /app01/atm
tree | head -n 100
Add Django DB settings via environment or `settings.py` (example):
```python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': '',
'USER': '',
'PASSWORD': os.getenv('ATM_DB_PASSWORD'),
'HOST': '',
'PORT': '',
}
}
Set the env var securely (e.g., in a service EnvironmentFile):
echo 'ATM_DB_PASSWORD=<STRONG_DB_PASSWORD>' | sudo tee /etc/sysconfig/atm
sudo chmod 600 /etc/sysconfig/atmcd /app01/atm
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r atm/requirements.txt # or your requirements file
# DB migrations & superuser
cd atm
python manage.py migrate
python manage.py collectstatic --noinput # for admin & static assets
# python manage.py createsuperuser # run once if neededCreate a systemd unit so the app runs as a service.
# /etc/systemd/system/atm_gunicorn.service
[Unit]
Description=Gunicorn for ATM (Django)
After=network.target
[Service]
User=<LINUX_USER>
Group=<LINUX_USER>
WorkingDirectory=/app01/atm/atm
EnvironmentFile=/etc/sysconfig/atm
Environment="DJANGO_SETTINGS_MODULE=atm.settings"
ExecStart=/app01/atm/venv/bin/gunicorn \
--workers 3 \
--bind \
--timeout 120 \
atm.wsgi:application
Restart=always
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now atm_gunicorn
sudo systemctl status atm_gunicorn --no-pagerTip
Logs: journalctl -u atm_gunicorn -e -n 100
Example host file:
# /etc/nginx/conf.d/atm.conf
server {
listen ;
server_name <SERVER_IP>;
# Serve the React build
root /app01/atm/Atm_fproject/dist;
index index.html;
location / {
try_files $uri /index.html;
}
# Backend API (prefix with /api/ in frontend calls)
location /api/ {
proxy_pass http:// /;
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;
}
# Django Admin (optional direct mapping)
location /admin/ {
proxy_pass http:// /admin/;
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;
}
# Static & media served by Nginx
location /static/ {
alias /app01/atm/atm/staticfiles/;
}
location /media/ {
alias /app01/atm/atm/media/;
}
}Apply and restart:
sudo nginx -t
sudo systemctl restart nginxOpen firewall (if applicable):
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reloadSet the API base URL for production (React Vite uses VITE_ prefix):
# /app01/atm/Atm_fproject/.env.production
VITE_API_BASE_URL=http://<SERVER_IP>Build the static assets:
cd /app01/atm/Atm_fproject
npm ci || npm install
npm run build
# Output goes to: /app01/atm/Atm_fproject/dist/Note
If you change VITE_API_BASE_URL, rebuild the frontend.
- Frontend:
http://<SERVER_IP>/ - Admin:
http://<SERVER_IP>/admin/
# Service startup on boot
sudo systemctl enable atm_gunicorn
sudo systemctl enable nginx- Remove any dev
.envfiles from the server - Rotate default credentials, enforce strong passwords
- Confirm
DEBUG=Falseand correctALLOWED_HOSTS - Consider Fail2ban/OS hardening, swap, and monitoring
Oneโoff backup (code + DB):
# Code snapshot
sudo tar -C /app01 -czf /app01/atm_backup_$(date +%F).tar.gz atm
# DB dump
mysqldump -u root -p atm > ~/atm_db_backup_$(date +%F).sql
# Quick verification
ls -lh ~/atm_db_backup_$(date +%F).sql
head ~/atm_db_backup_$(date +%F).sql | sed -n '1,8p'Integrity checks (example from your logs):
# Compare file counts between live and extracted backup (illustrative)
find /app01/atm | wc -l
find /app01/atm_backup_$(date +%F) | wc -lAutomate with a simple script + cron:
# /usr/local/sbin/atm_backup.sh
#!/usr/bin/env bash
set -euo pipefail
STAMP=$(date +%F)
CODE_TGZ=/app01/atm_backup_${STAMP}.tar.gz
DB_SQL=~/atm_db_backup_${STAMP}.sql
tar -C /app01 -czf "$CODE_TGZ" atm
mysqldump -u root -p"${MYSQL_ROOT_PASSWORD}" atm > "$DB_SQL"
chmod 600 "$DB_SQL"
echo "Code: $CODE_TGZ"; ls -lh "$CODE_TGZ"
echo "DB: $DB_SQL"; ls -lh "$DB_SQL"sudo chmod +x /usr/local/sbin/atm_backup.sh
sudo crontab -e
# Run at 02:00 every day
0 2 * * * MYSQL_ROOT_PASSWORD='<root_pw>' /usr/local/sbin/atm_backup.sh >> /var/log/atm_backup.log 2>&1Tip
For large projects consider storing backups offโbox (S3, rsync to another host) and testing restores.
sequenceDiagram
autonumber
participant U as Browser (User)
participant N as Nginx (Port )
participant G as Gunicorn ()
participant D as Django (ATM)
participant M as MySQL
U->>N: GET /
N-->>U: Serve React build (dist/index.html)
U->>N: XHR to /api/*
N->>G: proxy_pass /api/*
G->>D: WSGI request
D->>M: Query
M-->>D: Rows
D-->>G: JSON
G-->>N: Response
N-->>U: JSON payload
- MySQL running (
mysqld), DB + user created - Python venv,
pip install,migrate,collectstatic -
atm_gunicorn.serviceactive and enabled - Nginx config points
rootto Vitedist, proxies/api/and/admin/ - Frontend built with correct
VITE_API_BASE_URL -
DEBUG=False,ALLOWED_HOSTSset - Firewall open on 80, services enabled on boot
- Backups scheduled and verified
Status: Deployment complete and verified. For version upgrades (Python/Django/Node), update packages, rebuild the frontend, run DB migrations, and reload services accordingly.
-
Gunicorn (Green Unicorn) is a Python WSGI HTTP server.
It runs the Django backend and handles multiple requests efficiently.
Think of it as the bridge between Django code and the outside world. -
Nginx is a high-performance web server and reverse proxy.
It serves the React frontend (static files) and forwards API requests to Gunicorn.
It also handles load balancing, caching, and security (SSL, headers, etc.).
๐ Flow:
Client โ Nginx โ Gunicorn โ Django โ MySQL