Digital internal control system for Norwegian restaurants. Replaces paper-based logbooks for food safety (IK-Mat) and alcohol service compliance (IK-Alkohol) with a structured, role-aware web application.
Built for Everest Sushi & Fusion AS, Innherredsveien 1, Trondheim, as part of IDATT2105 Full-Stack Application Development at NTNU.
Team: Dennis Moe · Oleander Tengesdal · Stian Walmann · Sindre Jentoft Bøe
Live demo: https://stain.no hosted on Railway — log in with the test credentials below.
- Temperature logging — Daily freezer and refrigerator readings with automatic out-of-range detection
- Checklists — Daily, weekly, and monthly task lists for kitchen opening, closing, and HACCP compliance
- Deviations — Report, track, and resolve non-conformances with severity levels and a resolution workflow
- Temperature graphs — 7-day and 30-day chart views of all storage unit readings with alert markers
- Training documents — Employee certification tracking and document library
- IK-Alkohol module — Age-check logs, beverage storage inspections, and bartender compliance checklists
- Admin settings — Storage unit CRUD, user management with per-user permissions, organisation profile, and checklist template management
| Layer | Technology |
|---|---|
| Frontend | Vue 3 (Composition API, <script setup lang="ts">) + Vite |
| State | Pinia |
| Routing | Vue Router 4 |
| HTTP | Axios with JWT interceptors |
| Charts | Chart.js + vue-chartjs |
| CSS | Hand-crafted design system — no Tailwind, no Bootstrap |
| Backend | Java 21 + Spring Boot 3 + Spring Security + JWT |
| Database | MySQL 8 with Flyway migrations |
| Build | Maven 3.9 |
| Tests | Vitest (frontend unit), Cypress (frontend E2E), JUnit 5 (backend) |
| CI/CD | GitHub Actions — lint on PR, backend tests on PR |
| Deployment | Docker Compose |
Requirements: Docker Desktop (or Docker Engine + Compose plugin).
cp .env.example .env
docker compose up --buildThe .env.example file contains working defaults — no edits needed for a local demo run. The first build takes a few minutes while Maven downloads dependencies.
Services after startup:
- Frontend: http://localhost:5173
- Backend API: http://localhost:8080
- Swagger UI: http://localhost:8080/swagger-ui.html
- MySQL: localhost:3306
Note: Seed data (demo users, units, readings, deviations) is loaded automatically on first startup via Flyway migrations. Login credentials are listed below.
Backend
Requirements: Java 21, Maven 3.9, MySQL 8 running locally.
Create the database and user:
CREATE DATABASE ik_kontroll CHARACTER SET utf8mb4;
CREATE USER 'ik_user'@'localhost' IDENTIFIED BY 'ik_pass';
GRANT ALL PRIVILEGES ON ik_kontroll.* TO 'ik_user'@'localhost';Start with the local and dev Spring profiles (dev runs seed migrations, local provides the datasource URL):
cd backend
mvn spring-boot:run -Dspring-boot.run.profiles=local,devThe backend starts on port 8080. Flyway runs schema and seed migrations automatically on first start.
Frontend
Requirements: Node 20+.
cd frontend
npm install
npm run devThe frontend starts on http://localhost:5173. Vite proxies all /api requests to http://localhost:8080, so no CORS configuration is needed in development.
| Variable | Required | Description |
|---|---|---|
MYSQL_ROOT_PASSWORD |
Docker only | MySQL root password for the database container |
MYSQL_DATABASE |
Docker only | Database name (default: ik_kontroll) |
MYSQL_USER |
Docker only | App database user (default: ik_user) |
MYSQL_PASSWORD |
Docker only | App database password (default: ik_pass) |
JWT_SECRET |
Production | JWT signing secret — must be 32+ characters |
MAIL_USERNAME |
Optional | Gmail address for sending invite emails |
MAIL_PASSWORD |
Optional | Gmail App Password (not your account password) |
Set the following environment variables on the backend service. If using Railway's MySQL plugin, reference its variables for the datasource:
SPRING_PROFILES_ACTIVE=dev
SPRING_DATASOURCE_URL=jdbc:mysql://<host>:<port>/<database>?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8&useUnicode=true
SPRING_DATASOURCE_USERNAME=<db-user>
SPRING_DATASOURCE_PASSWORD=<db-password>
SPRING_JPA_HIBERNATE_DDL_AUTO=validate
JWT_SECRET=<random-32-char-secret>
MAIL_USERNAME=noreply.iksys@gmail.com
MAIL_PASSWORD=<gmail-app-password>
All accounts belong to the demo organisation "Everest Sushi & Fusion AS".
| Password | Role | Access level | |
|---|---|---|---|
| kari@everestsushi.no | admin123 | ADMIN | Full access including settings, user management, and checklist template management |
| ola@everestsushi.no | leder123 | MANAGER | All operational views — temperature logging, checklists, deviations, graphs, training |
| per@everestsushi.no | ansatt123 | STAFF | Core operational views — temperature logging, checklists, deviations |
33 tests covering auth store hydration, readings store, deviations store, and the usePermission composable.
cd frontend
npm test # run once
npm run test:watch # watch mode
npm run test:coverage # with coverage reportCoverage is approximately 84% across tested modules (stores and composables).
23 specs across 4 test files. Requires the full application stack (frontend + backend + seeded database) to be running.
# Start the stack first, then:
cd frontend
npm run cy:open # interactive browser mode
npm run cy:run # headless CI modeTest files:
cypress/e2e/auth.cy.ts— login, logout, redirect guardscypress/e2e/temperature.cy.ts— unit selection, form validation, successful reading submissioncypress/e2e/checklists.cy.ts— frequency filtering, item toggling, optimistic updatescypress/e2e/deviations.cy.ts— report form, severity selection, list display
cd backend
mvn clean testTest reports are written to backend/target/surefire-reports/. Coverage exceeds 50% as required by the course specification.
Two GitHub Actions workflows run on every pull request to main:
- lint.yml — TypeScript type-check (
npm run type-check) on the frontend - test.yml —
mvn clean teston the backend, publishes JUnit XML results as PR check
/
├── .github/workflows/ GitHub Actions CI
├── backend/ Spring Boot application
│ ├── src/main/java/ Feature-package architecture
│ │ ├── auth/ JWT authentication
│ │ ├── checklist/ Checklist templates + instances
│ │ ├── temperature/ Temperature readings
│ │ ├── deviations/ Deviation tracking
│ │ ├── alcohol/ IK-Alkohol compliance
│ │ ├── units/ Storage unit management
│ │ ├── user/ User management + permissions
│ │ ├── dashboard/ Aggregated summary data
│ │ ├── reports/ PDF/JSON export
│ │ └── config/ Security, JWT, Swagger
│ └── src/main/resources/
│ ├── db/migration/ Flyway schema migrations
│ └── db/migration-dev/ Development seed data
├── frontend/ Vue 3 application
│ ├── src/
│ │ ├── views/ Page components (one per route)
│ │ ├── stores/ Pinia state stores
│ │ ├── services/ API abstraction layer
│ │ ├── types/ TypeScript interfaces
│ │ ├── components/ Shared UI components
│ │ └── assets/main.css Design system (CSS custom properties)
│ ├── cypress/ E2E tests
│ └── documentation/ Frontend architecture docs
├── db/ Database Dockerfile
└── docker-compose.yaml Full-stack Docker setup
When the backend is running, Swagger UI is available at:
http://localhost:8080/swagger-ui.html
The OpenAPI spec (JSON) is at http://localhost:8080/v3/api-docs.
Detailed documentation lives in frontend/documentation/:
| File | Contents |
|---|---|
08-architecture-diagrams.md |
Architecture diagrams — system overview, backend domain model (ER), frontend route structure, 5 user flow diagrams, JWT session sequence |
10-prioritization.md |
Prioritization and development process — how the team approached the project, phase-by-phase decisions, what was built and why, what was intentionally left out |
01-architecture.md |
System architecture, directory structure, layout modes, state management patterns, key design decisions |
02-auth-permissions.md |
JWT authentication flow, role-based access control, two-layer permissions system |
03-api-contracts.md |
Full REST API contract — every endpoint, request/response shape, error codes |
04-features/ |
Feature-level documentation for each module (temperature, checklists, deviations, graphs, training, settings) |
05-design-system.md |
CSS design system, tokens, component classes |
06-state-management.md |
Pinia store patterns, data flow |
09-testing.md |
Testing guide — how to run all tests, what each test suite covers, known limitations |
- JWT stored in
sessionStorage(notlocalStorage) — sessions expire when the browser tab is closed, per assignment specification section 4.7 - All API endpoints require
Authorization: Bearer {token}exceptPOST /api/auth/login - Multi-tenant isolation enforced at the backend — every query is scoped to the authenticated user's
organizationIdfrom the JWT claims - Input validation using Spring Validation (
@Valid,@NotBlank,@Size, etc.) on all request bodies - CORS configured for the frontend origin
- SQL injection prevented by JPA prepared statements
- XSS prevention via Content-Security-Policy response headers
Course: IDATT2105 Full-Stack Application Development
Institution: NTNU
Deadline: Friday 10 April 2026, kl. 14:00 (Inspera)
Client: Everest Sushi & Fusion AS (org.nr. 937 219 997)