← Click here to go back to Home Page Documentation
College Logo
SALT
Salvation Army SALT College of Africa

Technical Documentation

System Architecture, Deployment & CI/CD Guide

Document ID:SALT-TECH-2026-001
Version:1.0
Date:20th February 2026
Prepared By:Sumba Group Limited
Developed BySumba Group Limited
Classification:Internal — Confidential

Table of Contents

1. System Architecture Overview 2. Data Flow Diagrams 2.1 Student Enrollment Flow 2.2 Assignment Lifecycle 2.3 Exam Lifecycle 2.4 Certificate Generation 3. Database Schema Diagram 4. Technology Stack 5. Environment Configuration 6. CI/CD Pipeline 7. EC2 Deployment Guide 7.1 EC2 Instance Setup 7.2 Server Prerequisites 7.3 Database Setup 7.4 Backend Service 7.5 Frontend Setup 7.6 Nginx Reverse Proxy 7.7 SSL Certificate 8. API Endpoint Reference 9. Database Migrations 10. Security Architecture 11. File Storage Architecture 12. Monitoring & Troubleshooting 13. Notification Architecture 13.1 FCM Topic Model 13.2 Backend Trigger Points 13.3 Executive Dashboard Firebase Setup

1. System Architecture Overview

The platform is a monolithic Spring Boot application serving REST APIs to three frontend clients. All data flows through a single API gateway on port 8091.

CLIENT LAYER Next.js Web Admin Port 8081 • 35 pages Flutter Mobile Student • 9 screens Exec Dashboard Flutter • Analytics Legacy JSF (Deprecated) Spring Boot 3.4.2 Port 8091 • /SaltELearnAppApi • JWT Auth 15 Controllers • 27 Services • 48 Repositories Security Layer JWT Filter • BCrypt Brute Force Protection DATA & STORAGE LAYER PostgreSQL 17 DB: salvation • 35+ tables Flyway V1-V16 • Port 5432 Redis 7 Session Cache • TTL Policies Port 6379 MinIO (S3) Bucket: salt-files Fallback: /opt/salt_files EXTERNAL SERVICES Django Notification Server (SMS/Email) Firebase Cloud Messaging (FCM) Let's Encrypt SSL (Nginx) LOCAL: docker-compose STAGING: stagging.saltcollegeandresourcecentre.com (EC2) PRODUCTION: app.saltcollegeandresourcecentre.com (EC2)
Figure 1: System Architecture — Three-tier monolithic application with external notification services

2. Data Flow Diagrams

2.1 Student Enrollment Flow

Student Register OTP Select Subjects ETO Approves Status: N → EP Admin Approves Status: EP → P Status Flow: N EP P D N=New • EP=ETO Approved • P=Admin Approved • D=Done Rejection: ER (ETO) / AR (Admin) Guard: Cannot reject P/D status
Figure 2: Student Enrollment & Approval Data Flow

2.2 Assignment Lifecycle

Tutor UploadsAssignment Q MinIO StorePDF/DOCX file Student SeesDownloads & works Student UploadsAnswer file Tutor MarksScore 0-100 Admin VerifiesFinal score Status: N T P N=New • T=Tutor Marked • P=Admin Verified • R=Rejected (resubmit) Limit: max 3 assignments per subject per month Tables: tbl_materials_upload (question) • tbl_assignment_marking (answer/grade) • Files stored in MinIO salt-files bucket
Figure 3: Assignment Lifecycle Data Flow

2.3 Exam Lifecycle

Tutor CreatesPaper + MCQs Admin ApprovesPaper review Set Exam DateTime window Students TakeMCQ or PDF Auto-GradeMCQ only Tutor ReviewsEssay marking Final GradeWeighted formula Grading Formula (admin-configurable weights from tbl_sys_settings) total = (assignment_avg x assignment_weight_pct / 100) + (exam_score x exam_weight_pct / 100) → Grade lookup from tbl_grading_config Retake Policy: Failed students can retake up to 3 times. After 3rd failure → Oral exam (manual score entry by admin/tutor). Tables: tbl_exam_paper, tbl_questions, tbl_student_quiz
Figure 4: Exam Lifecycle Data Flow with Grading Formula

2.4 Certificate Generation

All Subjects PassScore >= pass_mark Admin GeneratesCertificateService PdfGeneratorOpenPDF + ZXing QR Store in MinIOtbl_certificates Public QR Verify/v2/certificates/verify/{serial} QR encodes: Student Name • Grade • Certificate Serial • Admission Number • Verification URL
Figure 5: Certificate Generation & Public Verification Flow

3. Database Schema Diagram

Database — Entity Relationship Diagram (PostgreSQL 17) salvation database • 35+ tables • All PKs bigint with sq_* sequences • Entities extend Auditable<String> Original Table New (Flyway V1-V16) FK AUTH & USERS tbl_sys_users PK id (bigint) user_name (varchar) email_address (varchar) password (varchar, BCrypt) f_name, l_name (varchar) role (varchar) FK level_id → tbl_user_level FK user_territory_id → tbl_territory tbl_user_level PK id (bigint) level_name (varchar) access_level (int) 5=Admin 8=Tutor 9=ETO 12=Student tbl_student PK id (bigint) admission_no (varchar) admission_status (varchar) email, entry_type FK user_id → tbl_sys_users FK rank_id → tbl_tbl_ranks FK session_id → tbl_sessions (new) tbl_tbl_ranks PK id (bigint) rank_name (varchar) rank_code (varchar) GEOGRAPHY tbl_countries PK id (bigint) country_name (varchar) country_code (varchar) flag_emoji, phone_code (new) tbl_territory PK id (bigint) territory_name (varchar) territory_code (varchar) FK country_id → tbl_countries ACADEMIC & CURRICULUM tbl_learning_level PK id (bigint) level_name (varchar) level_index (int) tbl_subjects PK id (bigint) subject_code (varchar) subject_name (varchar) subject_cost (decimal) FK learning_level_id → tbl_learning_level tbl_academic_level PK id (bigint) FK student_id → tbl_student FK learning_level_id → tbl_learning_level status (varchar) tbl_student_subjects_selection (CORE TABLE) PK id (bigint) FK academic_level_id → tbl_academic_level FK subjects_id → tbl_subjects marks, status, suspension_deadline (new), retake_count (new) EXAMINATIONS tbl_exam_paper PK id (bigint) FK subject_id → tbl_subjects paper_name (varchar) is_downloadable (bool, new) download_pdf_path (new) upload_answer_deadline (new) tbl_questions PK id (bigint) FK paper_id → tbl_exam_paper question (text) a, b, c, d (varchar) correct_answer (varchar) tbl_student_quiz PK id (bigint) FK paper_id → tbl_exam_paper score, status, start/end time tbl_paper_answer PK id (bigint) FK quiz_id → tbl_student_quiz selected_answer (varchar) tbl_exam_dates PK id (bigint) exam_date, paper_id FK MATERIALS & ASSIGNMENTS tbl_materials_upload PK id (bigint) FK subject_id → tbl_subjects material_type (A=Assgn, R=Read) file_path, title tbl_assignment_marking PK id (bigint) FK material_id FK student_id marks, status (N/R/T/P) tbl_late_assignment PK id (bigint) student_id, material_id, status tbl_tutor_subject_assignment PK id (bigint) tutor_id FK, subject_id FK COMMUNICATION tbl_chat PK id (bigint) sender_id, receiver_id, message tbl_announcements PK id (bigint) title, body, territory_id FK tbl_sms_log PK id • phone, message, status tbl_login_logs PK id • user_id, ip, login_date CONFIGURATION & SYSTEM tbl_sys_settings PK id = 1 (SINGLETON) signing_key, token_expiry grading weights (new cols) tbl_languages PK id (bigint) language_name, lang_code tbl_learning_date PK id • start_date, end_date tbl_sys_intrusion PK id • ip, user, attempt_date NEW TABLES — Added via Flyway Migrations V1–V16 tbl_sessions PK id (bigint, sq_sessions) session_name (varchar, UNIQUE) status (A=Active, I=Inactive) tbl_grading_config PK id (bigint) grade_name (varchar) min_score, max_score (decimal) display_order (int) tbl_billing PK id (bigint, sq_billing) FK student_id → tbl_student FK academic_level_id total_amount, is_new_student tbl_billing_items PK id (bigint) FK billing_id → tbl_billing FK subject_id → tbl_subjects tbl_certificates PK id (bigint) FK student_id → tbl_student serial_number (UNIQUE) certificate_type (CERT/TRANS) qr_code_data, pdf_path tbl_suspension_log PK id (bigint) FK student_id → tbl_student FK subject_selection_id status (SUSPENDED/LIFTED/DECLINED) admin_comments, lifted_date tbl_student_achievements PK id (bigint) FK student_id → tbl_student achievement_text (text) achievement_date, entered_by FK tbl_intake_config PK id (bigint) intake_month (int, 1-12) start_date, end_date (date) behavior (BLOCK/WARNING) tbl_chatbot_faq PK id (bigint) question_pattern (text) answer_text (text) category, language tbl_chatbot_escalations PK id (bigint) FK student_id → tbl_student FK assigned_to → tbl_sys_users status (OPEN/IN_PROGRESS/RESOLVED) initial_question, image_path tbl_data_integrity_log PK id (bigint) check_type (DUPLICATE/ORPHAN/...) severity (INFO/WARN/ERROR) table_name, record_id, auto_resolved Key Status Flows Admission Status: U (Uploaded) → N (New) → EP (ETO Pending) → P (Approved) | D (Deleted) | AR/ER (Rejected) Subject Selection: A (Active) → P (Pass) / E (Enrolled) / N (New) / R (Rejected) / D (Done) Assignment Marking: N (New) → R (Received) → T (Tutor Marked) → P (Published) Certificate Type: CERTIFICATE | TRANSCRIPT Suspension: SUSPENDED | LIFTED | DECLINED Primary Key Convention All PKs: bigint with dedicated sequences (sq_*) All entities extend Auditable<String>: createdBy, createdDate, lastModifiedBy, lastModifiedDate JPA: @SequenceGenerator(allocationSize=1)
Figure 6: Database Entity Relationship Diagram — All 35+ tables including Flyway V1–V16 additions

4. Technology Stack

ComponentTechnologyVersionPurpose
BackendSpring Boot3.4.2REST API server (embedded Tomcat)
LanguageJava (Temurin)17 LTSBackend language
BuildMaven3.9+Build & dependency management
DatabasePostgreSQL17Primary data store (EOL Nov 2030)
CacheRedis7 (Alpine)Session & data caching with TTL
File StorageMinIOLatestS3-compatible object storage
MigrationsFlyway(bundled)Database schema versioning (V1-V16)
JWTjjwt0.12.6Token-based authentication (HS256)
PDFOpenPDF2.0.3Certificate & transcript generation
ExcelApache POI5.2.3Report & data export
QR CodesZXing3.5.3Certificate verification QR
Web FrontendNext.js16Admin panel (TypeScript, Tailwind CSS 4, App Router)
Mobile AppFlutter3.xStudent app (Riverpod 3.x, GoRouter, Dio)
Exec DashboardFlutter3.xAnalytics dashboard (fl_chart, Riverpod)
NotificationDjango ServerSMS/Email relay (Africa’s Talking)
HTTP ClientRetrofit2 / OkHttp32.11.0 / 4.12.0External API calls
AnnotationsLombok1.18.36Boilerplate reduction

5. Environment Configuration

5.1 Centralized Configuration

All environment-specific values are centralized in a .env file at the project root. Docker Compose uses ${VAR:-default} syntax to reference these values. Spring Boot uses profile-specific application-{profile}.properties files.

Security: The .env file is in .gitignore and must NEVER be committed. Use .env.example as a template. Production credentials go in .env.production on the server only.

5.2 Environment Matrix

EnvironmentDB HostDB NameAPI PortWeb PortProfileURL
Local (Docker)localhostsalvation80918081testhttp://localhost:8091
Developmentlocalhostsalvation80918081localhosthttp://localhost:8091
Staging (EC2)localhostsalvation80918081productionhttps://stagging.saltcollegeandresourcecentre.com (IP: 3.13.144.24)
Production (EC2)172.31.25.145salvation80918081productionhttps://app.saltcollegeandresourcecentre.com (IP: 3.13.155.123)

5.3 Environment Variables

VariableDefaultDescription
POSTGRES_DBsalvationPostgreSQL database name
POSTGRES_USERpostgresDatabase username
POSTGRES_PASSWORDDatabase password (from .env)
REDIS_HOSTlocalhostRedis server host
REDIS_PORT6379Redis port
MINIO_ROOT_USERminioadminMinIO admin username
MINIO_ROOT_PASSWORDminioadminMinIO admin password
MINIO_BUCKETsalt-filesMinIO bucket name
SERVER_PORT8091Spring Boot server port
CONTEXT_PATH/SaltELearnAppApiAPI context path

5.4 Spring Boot Profiles

ProfileFileUse Case
test (default)application-test.propertiesDocker Compose local development
localhostapplication-localhost.propertiesNative local development (no Docker)
productionapplication-production.propertiesEC2 staging and production servers

6. CI/CD Pipeline

6.1 Pipeline Overview

GitHub Actions automates build and deployment. The pipeline is defined in .github/workflows/deploy.yml.

Developergit push GitHubmain / production GitHub Actionsubuntu-latest Build JARmvn clean package SSH + RsyncDeploy to EC2 systemctl restartbackend service main branch → Test/Staging EC2 (/opt/backend/SaltELearnAppApi.jar) production branch → Production EC2 (/opt/SaltELearnAppApi.jar)
Figure 7: CI/CD Pipeline — GitHub Actions deployment flow

6.2 Pipeline Steps (deploy.yml)

#StepActionDetails
1Checkoutactions/checkout@v3Clone repository
2Setup JDKactions/setup-java@v3Temurin JDK 17
3Buildmvn clean package -DskipTestsGenerate JAR file
4Setup SSHWrite private key from secretsssh-keyscan for known_hosts
5Deployrsync -avzCopy JAR to EC2 via SSH
6Restartsudo systemctl restart backendRestart Spring Boot service

6.3 Required GitHub Secrets

SecretPurposeUsed By
TEST_SERVER_SSH_KEYSSSH private key (ed25519) for staging serverdeploy-test job
SSH_TEST_HOSTStaging server IP/hostnamedeploy-test job
PROD_SERVER_SSH_KEYSSSH private key (RSA) for production serverdeploy-production job
SSH_PROD_HOSTProduction server IP/hostnamedeploy-production job
SSH_USERSSH username (shared across both)Both jobs
Branch strategy: Push to main deploys to staging. Push to production deploys to production. Use pull requests for code review before merging.

7. EC2 Deployment Guide

7.1 EC2 Instance Setup

  1. Launch Ubuntu 22.04 LTS EC2 instance (t3.medium minimum: 2 vCPU, 4GB RAM)
  2. Configure Security Group inbound rules:
    PortProtocolSourcePurpose
    22TCPTeam IPs onlySSH access
    80TCP0.0.0.0/0HTTP (redirect to HTTPS)
    443TCP0.0.0.0/0HTTPS (Nginx reverse proxy)
    8091TCPlocalhost onlySpring Boot API (internal)
    8081TCPlocalhost onlyNext.js frontend (internal)
    8090TCPlocalhost onlyJenkins CI/CD (internal)
    9000TCPlocalhost onlyMinIO S3 API (internal)
    9001TCPlocalhost onlyMinIO Console (internal)
  3. Allocate Elastic IP for stable DNS resolution
  4. Configure DNS: stagging.saltcollegeandresourcecentre.com → Elastic IP

7.2 Server Prerequisites

# System update
sudo apt update && sudo apt upgrade -y

# Java 17 (Temurin)
sudo apt install -y openjdk-17-jdk
java -version   # Verify: openjdk 17.x

# PostgreSQL 17
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt update && sudo apt install -y postgresql-17
sudo systemctl enable postgresql

# Redis 7
sudo apt install -y redis-server
sudo systemctl enable redis-server

# Node.js 20+ (for Next.js)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

# MinIO
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio && sudo mv minio /usr/local/bin/

# Nginx + Certbot
sudo apt install -y nginx certbot python3-certbot-nginx
sudo systemctl enable nginx

# PM2 (Node.js process manager)
sudo npm install -g pm2

7.3 Database Setup

# Create database
sudo -u postgres psql -c "CREATE DATABASE salvation;"

# Set password
sudo -u postgres psql -c "ALTER USER postgres PASSWORD '20dev@22Kenya!';"

# Load production data
sudo -u postgres psql salvation < /path/to/salvation.sql

# Verify
sudo -u postgres psql -d salvation -c "SELECT count(*) FROM tbl_sys_users;"

7.4 Backend Service Setup

Create a systemd service for the Spring Boot backend:

# /etc/systemd/system/backend.service
[Unit]
Description=E-Learning Backend API
After=network.target postgresql.service redis.service

[Service]
User=ubuntu
WorkingDirectory=/opt/backend
ExecStart=/usr/bin/java -jar -Dspring.profiles.active=production SaltELearnAppApi.jar
SuccessExitStatus=143
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable backend
sudo systemctl start backend

# Verify
sudo systemctl status backend
curl -s http://localhost:8091/SaltELearnAppApi | head

7.5 Next.js Frontend Setup

# Clone/deploy web-admin to /opt/web-admin
cd /opt/web-admin
npm install
npm run build

# Start with PM2 (persistent across reboots)
pm2 start npm --name "salt-web" -- start -- --port 8081
pm2 save
pm2 startup  # Generates startup script for OS

# Verify
curl -s http://localhost:8081 | head

7.6 Nginx Reverse Proxy

The nginx configuration file is maintained in the repository at nginx/sites-available/stagging.saltcollegeandresourcecentre.com.conf. Copy it to the server:

# Copy config to server
sudo cp nginx/sites-available/stagging.saltcollegeandresourcecentre.com.conf \
  /etc/nginx/sites-available/

# Create symlink
sudo ln -sf /etc/nginx/sites-available/stagging.saltcollegeandresourcecentre.com.conf \
  /etc/nginx/sites-enabled/

# Remove default site
sudo rm -f /etc/nginx/sites-enabled/default

# Increase upload size in nginx.conf (already set)
# client_max_body_size 500M;

# Test and reload
sudo nginx -t
sudo systemctl restart nginx

Upstream Routing Summary:

UpstreamInternal PortNginx PathService
frontend_backend127.0.0.1:8081/Next.js Web Admin (PM2)
backend127.0.0.1:8091/SaltELearnAppApi/Spring Boot API (systemd)
jenkins_backend127.0.0.1:8090/jenkins/Jenkins CI/CD
(direct)127.0.0.1:9001/minio/MinIO Console

Key nginx features:

7.7 SSL Certificate (Let's Encrypt)

# Generate certificate
sudo certbot --nginx -d stagging.saltcollegeandresourcecentre.com

# Auto-renewal cron (runs twice daily)
sudo crontab -e
# Add: 0 0,12 * * * certbot renew --quiet

# Verify renewal
sudo certbot renew --dry-run

8. API Endpoint Reference

Base URL: https://stagging.saltcollegeandresourcecentre.com/SaltELearnAppApi

Authentication (/auth/*) — No JWT required

MethodPathDescription
POST/auth/authenticateStep 1: Send credentials, triggers OTP
POST/auth/validate_access_codeStep 2: Validate OTP, returns JWT
POST/auth/change_passwordChange password (after MCP flag)
POST/auth/log_outInvalidate session
POST/auth/forgot_passwordRequest password reset

V2 Bridge (/v2/bridge/*) — JWT required

MethodPathDescription
GET/v2/bridge/usersList all users
GET/v2/bridge/studentsList students (paginated)
GET/v2/bridge/students/pendingPending approvals (N, EP status)
POST/v2/bridge/students/{id}/approveApprove student enrollment
POST/v2/bridge/students/{id}/rejectReject student enrollment
GET/v2/bridge/subjectsList all subjects
GET/v2/bridge/subjects/by-level/{levelId}Subjects by learning level
GET/v2/bridge/countriesCountry list (African first)
GET/v2/bridge/territoriesTerritory list
GET/v2/bridge/settingsSystem settings (singleton)

V2 Features (/v2/*) — JWT required

MethodPathDescription
GET/v2/grading/configGrading thresholds (6 tiers)
GET/v2/grading/student/{id}/summaryStudent grade summary
POST/v2/billing/calculate/{id}Calculate student billing
GET/v2/suspension/studentsSuspended students list
POST/v2/certificates/generate/{sId}/{lId}Generate certificate PDF
GET/v2/certificates/verify/{serial}Public QR verification (NO auth)
GET/v2/analytics/dashboardDashboard statistics
POST/v2/admin/schema/validateRun data integrity check
GET/v2/admin/schema/export/fullFull database backup (ZIP)

Admin (/admin/*) — JWT required

MethodPathDescription
POST/admin/restore_students/{userId}Upload CSV previous performance
POST/admin/update_student_emailAdmin updates student email
Full endpoint listing: 154+ endpoints across 15 controllers + 12 V2 controllers. See the Technical Documentation for complete package structure.

9. Database Migrations

VersionFileDescription
V1V1__create_sequences_for_new_tables.sql10 new sequences (sessions, grading, billing, certificates, etc.)
V2V2__create_new_tables.sql10 new tables (sessions, grading_config, billing, certificates, suspension, intake, achievements, chatbot_faq, chatbot_escalations)
V3V3__add_columns_to_existing_tables.sqlAdd columns: session_id, suspension fields, exam download fields, sys_settings params, country flags
V4V4__create_indexes.sqlPerformance indexes on frequently queried columns
V5V5__seed_grading_config.sqlSeed 6-tier grading: High Distinction (85-100), Distinction (75-84), Credit (65-74), Merit (55-64), Pass (50-54), Fail (0-49)
V6V6__enrollment_bug_fixes.sqlBug 7 fixes: soft-delete columns (original_email, original_username), data recovery for students 2018333/2024053
V7V7__data_integrity_constraints.sqlPartial UNIQUE indexes, admission status change trigger, BOM cleanup trigger, data integrity log table
V8V8__add_language_columns.sqlLanguage-based subject filtering: language on tbl_subjects, preferred_language on tbl_student, auto-detect from subject_code
Migration rules: Never modify existing migrations. Always create new V{N}__ files. Use IF NOT EXISTS / IF NOT EXISTS for safety. Test against salvation.sql before applying to production.

10. Security Architecture

JWT Authentication Flow (2-Step OTP) Client Login /auth/authenticateValidates credentials Send OTPvia Django SMS/Email /auth/validate_otpVerifies access code JWT Issued (HS256)Key from tbl_sys_settings ID=1 Subsequent API Calls: Authorization: Bearer <token> → JwtAuthenticationFilter → verifyWith(secretKey) → Spring Security Context → Controller BCrypt password hashing Brute force protection Stateless sessions (no cookies) CORS: localhost, staging, production
Figure 8: JWT Authentication & Security Architecture

Public Routes (no JWT required)

/auth/**, /push/**, /ws/**, /common/**, /chat/**, /student/**, /v2/certificates/verify/**

All Other Routes

Require Authorization: Bearer <JWT> header. JWT signing key is stored in tbl_sys_settings (ID=1), padded to 256 bits minimum for HS256.

11. File Storage Architecture

MinIO Dual-Read Adapter (FileStorageService.java) UPLOAD Always → MinIO (salt-files) DOWNLOAD Try MinIO first Fallback: /opt/salt_files 404 not found? not found?
Figure 9: File Storage Dual-Read Strategy

Directory Structure (salt_files/)

salt_files/
├── +250/              # Rwanda uploads
├── +254/              # Kenya uploads
├── +257/              # Burundi uploads
├── +263/              # Zimbabwe uploads
├── SALT-College/      # Institution-level files
│   └── assignments/
├── student_assignments/  # Student submission files
└── student_reading/      # Reading materials

12. Monitoring & Troubleshooting

12.1 Service Health Checks

ServiceHealth CommandExpected Result
Backend APIcurl -s http://localhost:8091/SaltELearnAppApiHTML response (home page)
PostgreSQLpg_isready -U postgres -d salvationaccepting connections
Redisredis-cli pingPONG
MinIOcurl -s http://localhost:9000/minio/health/live200 OK
Next.jscurl -s http://localhost:8081HTML response
Nginxsudo nginx -tsyntax is ok

12.2 Log Locations

ServiceCommand
Backendsudo journalctl -u backend -f --no-pager
Nginxsudo tail -f /var/log/nginx/access.log /var/log/nginx/error.log
PostgreSQLsudo tail -f /var/log/postgresql/postgresql-17-main.log
Next.js (PM2)pm2 logs salt-web

12.3 Common Issues

IssueCauseSolution
502 Bad GatewayBackend not runningsudo systemctl restart backend
JWT token rejectedSigning key changedCheck tbl_sys_settings ID=1 signing key, restart backend
File upload failsMinIO bucket missingCreate bucket via MinIO console or mc mb local/salt-files
Flyway migration errorSchema conflictCheck flyway_schema_history, fix migration or add repair entry
OTP not receivedDjango server unreachableCheck app.saltcollegeandresourcecentre.com is reachable from EC2
CORS errorOrigin not whitelistedUpdate WebMvcConfig.java allowed origins
Out of memoryJVM heap too smallAdd -Xmx2g to ExecStart in backend.service
Connection pool exhaustedLeaked connectionsCheck HikariCP leak-detection-threshold setting, restart backend

12.4 Restart Commands

# Restart all services
sudo systemctl restart backend
sudo systemctl restart nginx
sudo systemctl restart redis
sudo systemctl restart postgresql
pm2 restart salt-web

# Full stack restart
sudo systemctl restart postgresql redis && sleep 2 && sudo systemctl restart backend && pm2 restart salt-web && sudo systemctl restart nginx

13. Notification Architecture

The platform uses Firebase Cloud Messaging (FCM) to deliver real-time push notifications to both the mobile app and the executive dashboard. The backend publishes notifications to FCM topics, and each client subscribes to the topics relevant to its role.

13.1 FCM Topic Model

Rather than maintaining individual device tokens for broadcast-style notifications, The platform uses FCM topics. Each client subscribes to a topic on startup, and the backend publishes messages to the appropriate topic.

FCM TopicSubscriberPurpose
salt_exec_eventsExecutive Dashboard (Flutter)Broadcasts key platform events to leadership: assignment submissions, assignment markings, and student approvals.
salt_notificationsMobile App (Flutter)General notifications for students, tutors, and ETOs: exam codes, approval status changes, announcements, and other role-specific alerts.

Topic subscriptions are managed client-side using the firebase_messaging Flutter package:

// Executive Dashboard - subscribes on app startup
FirebaseMessaging.instance.subscribeToTopic('salt_exec_events');

// Mobile App - subscribes on login
FirebaseMessaging.instance.subscribeToTopic('salt_notifications');

13.2 Backend Trigger Points

The backend sends notifications to the salt_exec_events topic from the NotificationService.notifyExecutive() method. This method is called from three locations in the codebase:

EventCalling Service / MethodTrigger ConditionNotification Payload
Assignment Submitted AssignnementService — called after student successfully submits assignment via /file_handler/submit_assignment Student uploads assignment file and record is saved in tbl_assignment_marking Student name, admission number, subject code, subject name, timestamp
Assignment Marked AssignnementService — called after tutor marks assignment via /v2/bridge/assignments/{id}/mark Tutor enters score and status changes to “T” (Tutor Marked) Tutor name, student name, subject code, score, timestamp
Student Approved (Admin) AdminController / UsersService — called when Admin gives final enrollment approval Student admission_status changes to “D” (Done/Approved) Student name, admission number, territory, approver role (“Admin”), timestamp
Student Approved (ETO) AdminController / UsersService — called when ETO approves student enrollment via /v2/bridge/students/{id}/approve Student passes ETO approval stage Student name, admission number, territory, approver role (“ETO”), timestamp

The NotificationService is an @Async service that runs notification dispatch on a separate thread pool to avoid blocking the main request thread. The FCM server key is stored in tbl_sys_settings (ID=1) and read on application startup.

Note: The notifyExecutive() method is fire-and-forget. If FCM delivery fails (e.g., network timeout), the failure is logged but does not roll back the triggering transaction (assignment submission, marking, or approval).

13.3 Executive Dashboard Firebase Setup

The Executive Dashboard Flutter app uses three Firebase/notification packages:

PackageVersionPurpose
firebase_coreLatest stableFirebase SDK initialization. Must be called in main() before any other Firebase usage.
firebase_messagingLatest stableFCM topic subscription, foreground/background message handling, and token management.
flutter_local_notificationsLatest stableDisplays local notification banners when the app is in the foreground (since FCM foreground messages do not show system notifications by default).

Initialization Flow

// main.dart
await Firebase.initializeApp();
await FirebaseMessaging.instance.subscribeToTopic('salt_exec_events');

// Request notification permission (iOS and web)
await FirebaseMessaging.instance.requestPermission(
  alert: true,
  badge: true,
  sound: true,
);

// Foreground message handler
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
  // Show local notification via flutter_local_notifications
  // Update badge count in NotificationProvider
});

Firebase Project & App Identifiers

PropertyValue
Firebase Project IDsalt-college-dashboard
Firebase Project Number1030054625511
Storage Bucketsalt-college-dashboard.firebasestorage.app

Platform-Specific App IDs

PlatformApp IDAPI KeyBundle / Package
Android1:1030054625511:android:794eb0bc62d9616c12e242AIzaSyCnoUFKFaISe5x7sY0j7SWSzsEVUeDBedknm.sumba.salf.executive_dashboard
iOS1:1030054625511:ios:40c54fb945f58e0412e242AIzaSyBt2arrfaU7HnzcOTK7Y_LQKwCuynojZVoke.co.sumba.salt.dashboard
Web1:1030054625511:web:32ecfb013c9ad1a412e242AIzaSyDv17i58vpEwz18uI540LZUpdjBvqcZB3IN/A

Web also includes: measurementId: G-2XE8S83MC7, authDomain: salt-college-dashboard.firebaseapp.com

Mobile App Firebase Identifiers

PlatformApp IDAPI KeyBundle / Package
Android1:986786589181:android:ed4ec0db21dec6b76e9194AIzaSyCAFyZSwlg5ejex9udsUuqFCKU71ZLoCFMelearning.sumba.co.ke
iOS1:1030054625511:ios:2f5ee0145354cf9a12e242AIzaSyBt2arrfaU7HnzcOTK7Y_LQKwCuynojZVoke.co.sumba.salt.mobile

Note: The mobile Android app uses a separate Firebase project (salt-college-e-learning, project number 986786589181). The mobile iOS app is registered under the dashboard project (salt-college-dashboard).

Platform Configuration Files

PlatformFileDashboard LocationMobile Location
Androidgoogle-services.jsonexecutive_dashboard/android/app/mobile/android/app/
iOSGoogleService-Info.plistexecutive_dashboard/ios/Runner/mobile/ios/Runner/
Webindex.html + firebase-messaging-sw.jsexecutive_dashboard/web/N/A
Dartfirebase_options.dartexecutive_dashboard/lib/mobile/lib/

iOS AppDelegate Configuration

Both apps require Firebase initialization in ios/Runner/AppDelegate.swift:

import FirebaseCore
import FirebaseMessaging

// Inside didFinishLaunchingWithOptions:
FirebaseApp.configure()

Android Gradle Configuration

Project-level build.gradle.kts:

plugins {
    id("com.google.gms.google-services") version "4.4.4" apply false
}

App-level build.gradle.kts:

plugins {
    id("com.google.gms.google-services")
}
dependencies {
    implementation(platform("com.google.firebase:firebase-bom:34.9.0"))
    implementation("com.google.firebase:firebase-messaging")
    implementation("com.google.firebase:firebase-analytics")
}
Important: The Firebase project must have Cloud Messaging API (V1) enabled in the Google Cloud Console. The legacy FCM API is deprecated. Ensure the backend uses the V1 HTTP API endpoint (https://fcm.googleapis.com/v1/projects/{project-id}/messages:send) for sending topic messages.

Appendix A — Web Admin Route Reference

This appendix lists all web admin routes used by the platform, organised by user role. These routes are internal navigation paths used by the Next.js Web Admin application.

Admin Routes (Access Level 5)

#Menu ItemRoute
1Dashboard/dashboard/admin
2Users/dashboard/admin/users
3Students/dashboard/admin/students
4Grading/dashboard/admin/grading
5Billing/dashboard/admin/billing
6Certificates/dashboard/admin/certificates
7Suspensions/dashboard/admin/suspension
8Intake Config/dashboard/admin/intake
9Sessions/dashboard/admin/sessions
10Achievements/dashboard/admin/achievements
11Subjects/dashboard/admin/subjects
12Exam Dates/dashboard/admin/exams/dates
13Exam Approvals/dashboard/admin/exams/approvals
14Student Exams/dashboard/admin/exams/student-approvals
15Reports/dashboard/admin/reports
16Announcements/dashboard/admin/announcements
17Countries/dashboard/admin/countries
18Territories/dashboard/admin/territories
19System Logs/dashboard/admin/logs
20SMS Log/dashboard/admin/sms-log
21Chat/dashboard/admin/chat
22Schema/dashboard/admin/schema
23Settings/dashboard/admin/settings

Tutor Routes (Access Level 8)

Menu ItemRoute
Dashboard/dashboard/tutor/
Assignments/dashboard/tutor/assignments
Exams/dashboard/tutor/exams
Students/dashboard/tutor/students
Materials/dashboard/tutor/materials
Chat/dashboard/tutor/chat

ETO Routes (Access Level 9)

Menu ItemRoute
Dashboard/dashboard/eto/
Approvals/dashboard/eto/approvals
Students/dashboard/eto/students
Enrollment/dashboard/eto/enrollment
Reports/dashboard/eto/reports
Chat/dashboard/eto/chat

Student Routes (Access Level 12)

Menu ItemRoute
Dashboard/dashboard/student/
Subjects/dashboard/student/subjects
Assignments/dashboard/student/assignments
Exams/dashboard/student/exams
Materials/dashboard/student/materials
Performance/dashboard/student/performance
Chat/dashboard/student/chat
Profile/dashboard/student/profile

Appendix B — Internal Status Codes

The platform uses single-character status codes internally. These codes appear in the database and API responses.

Admission Status (tbl_student.admission_status)

CodeMeaningDescription
UUploadedStudent record created via bulk CSV import
NNewStudent has self-registered, awaiting ETO review
EPETO PendingETO has approved, awaiting Admin final approval
PPending AdminAwaiting administrator approval
DDone / ApprovedFully approved and active

Assignment Status (tbl_assignment_marking)

CodeMeaningDescription
NNewAssignment submitted by student, not yet reviewed
RReturnedReturned to student for revision
TTutor MarkedTutor has graded the assignment
PApprovedAdmin has approved the final mark

Subject Selection Status (tbl_student_subjects_selection)

CodeMeaningDescription
AActiveStudent is actively enrolled in the subject
PPendingAwaiting approval
EExam ReadyStudent is eligible to sit the exam
NNewNewly registered
RRetakeStudent is retaking the subject
DDoneSubject completed

Password Change Flag (tbl_sys_users.mcp)

ValueMeaning
Y / yesMust change password on next login
N / noPassword not yet created, or already changed

Appendix C — Access Level Hierarchy

The platform uses numeric access levels for role-based access control (RBAC). A lower number indicates higher authority.

RoleAccess LevelTable: tbl_user_levelPermissions
Admin5 (highest)level = ‘Admin’Full platform management, final approvals, system settings
Tutor8level = ‘Tutor’Subject teaching, exam creation, assignment marking
ETO9level = ‘ETO’Territory-level oversight, first-level student approvals
Student12 (lowest)level = ‘Student’Learning, exams, assignments, chat
College E-Learning — Technical Documentation v1.0 — 20th February 2026
College E-Learning — Developed by Sumba Group Limited for Salvation Army SALT College of Africa