Set up CI
This commit is contained in:
parent
5dcf27bb9f
commit
52eb07a993
12 changed files with 223 additions and 8218 deletions
|
|
@ -2,13 +2,13 @@ name: Deploy to Pages
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ['main']
|
branches: ["main"]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ['main']
|
branches: ["main"]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: 'pages'
|
group: "pages"
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
@ -18,11 +18,6 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Validate static frontend
|
|
||||||
run: |
|
|
||||||
test -f frontend/index.html
|
|
||||||
test -f frontend/fizika.json
|
|
||||||
|
|
||||||
- name: Copy frontend to host pages mount
|
- name: Copy frontend to host pages mount
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
uses: http://forgejo:3000/andras/ci-actions/deploy-pages@main
|
uses: http://forgejo:3000/andras/ci-actions/deploy-pages@main
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,9 @@ name: Build and Publish Docker Image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ['main']
|
branches: ["main"]
|
||||||
tags: ['v*']
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ['main']
|
branches: ["main"]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
|
||||||
30
.forgejo/workflows/test.yml
Normal file
30
.forgejo/workflows/test.yml
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
name: Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: docker
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: https://code.forgejo.org/actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install root tooling
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Check formatting
|
||||||
|
run: npm run format:check
|
||||||
|
|
||||||
|
- name: Install backend dependencies
|
||||||
|
run: npm ci
|
||||||
|
working-directory: backend
|
||||||
|
|
||||||
|
- name: Run backend tests
|
||||||
|
run: npm test
|
||||||
|
working-directory: backend
|
||||||
10
.prettierignore
Normal file
10
.prettierignore
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
node_modules
|
||||||
|
backend/node_modules
|
||||||
|
|
||||||
|
# Lockfiles are managed by npm
|
||||||
|
package-lock.json
|
||||||
|
backend/package-lock.json
|
||||||
|
|
||||||
|
# Vendored / generated assets
|
||||||
|
frontend/js/jquery.min.js
|
||||||
|
*.min.js
|
||||||
6
.prettierrc.json
Normal file
6
.prettierrc.json
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false
|
||||||
|
}
|
||||||
105
README.md
105
README.md
|
|
@ -1 +1,106 @@
|
||||||
# Fizika
|
# Fizika
|
||||||
|
|
||||||
|
Practice web app for the Hungarian **emelt szintű fizika** (advanced-level
|
||||||
|
physics) written school-leaving exam. Students generate quizzes from a bank of
|
||||||
|
past exam questions — by topic, by exam paper, or as a random "érettségi"
|
||||||
|
set — answer them, and review their past results (stored locally in the
|
||||||
|
browser).
|
||||||
|
|
||||||
|
Live at <https://fizika.schmelczer.dev>.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
frontend/ Static site (HTML + CSS + jQuery, no build step).
|
||||||
|
Deployed to Pages. Fetches questions from the backend API.
|
||||||
|
|
||||||
|
backend/ Express server. Serves the admin panel and a small REST API for
|
||||||
|
editing questions and images. Reads/writes a single JSON file plus
|
||||||
|
an images directory.
|
||||||
|
```
|
||||||
|
|
||||||
|
The **backend is the single source of truth** for question data. The frontend
|
||||||
|
always fetches it from `GET /api/fizika` (see `frontend/js/load.js`); the
|
||||||
|
question JSON is no longer shipped with the static site.
|
||||||
|
|
||||||
|
### Access control
|
||||||
|
|
||||||
|
Authentication is handled at the reverse proxy (nginx + basic auth), not in the
|
||||||
|
app. The proxy exposes only two endpoints publicly; everything else (the admin
|
||||||
|
panel and all `/api/admin/*` routes) requires a login:
|
||||||
|
|
||||||
|
| Path | Access | Purpose |
|
||||||
|
| --------------- | ------- | -------------------------------- |
|
||||||
|
| `/api/fizika` | public | question data for the public app |
|
||||||
|
| `/api/pics/*` | public | question images |
|
||||||
|
| everything else | private | admin panel + edit API |
|
||||||
|
|
||||||
|
The app trusts the proxy, so **do not expose the backend port directly** without
|
||||||
|
adding authentication in front of it.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
| Method | Route | Description |
|
||||||
|
| -------- | ----------------------------- | --------------------- |
|
||||||
|
| `GET` | `/api/fizika` | All questions |
|
||||||
|
| `GET` | `/api/images` | List image filenames |
|
||||||
|
| `GET` | `/api/pics/:file` | Serve an image |
|
||||||
|
| `GET` | `/api/admin/questions` | All questions (admin) |
|
||||||
|
| `POST` | `/api/admin/questions` | Create a question |
|
||||||
|
| `PUT` | `/api/admin/questions/:id` | Update a question |
|
||||||
|
| `DELETE` | `/api/admin/questions/:id` | Delete a question |
|
||||||
|
| `POST` | `/api/admin/images/upload` | Upload an image |
|
||||||
|
| `DELETE` | `/api/admin/images/:filename` | Delete an image |
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The backend is configured via environment variables:
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
| -------------- | ------------------------- | --------------------------------- |
|
||||||
|
| `PORT` | `3001` | Port to listen on |
|
||||||
|
| `DATA_PATH` | `../frontend/fizika.json` | Path to the questions JSON file |
|
||||||
|
| `PICS_PATH` | `../frontend/pics` | Directory holding question images |
|
||||||
|
| `FRONTEND_URL` | `*` | Allowed CORS origin |
|
||||||
|
|
||||||
|
In production `DATA_PATH` and `PICS_PATH` point at mounted volumes that hold the
|
||||||
|
canonical data. The defaults are dev conveniences only — set both explicitly to
|
||||||
|
a JSON file and a directory that exist before starting.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd backend
|
||||||
|
npm install
|
||||||
|
DATA_PATH=/path/to/fizika.json PICS_PATH=/path/to/pics npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open `frontend/index.html` (or serve `frontend/` statically). On
|
||||||
|
`localhost` the frontend talks to the backend on port 3001 automatically.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Backend tests use Node's built-in test runner (no extra dependencies):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd backend
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Formatting
|
||||||
|
|
||||||
|
[Prettier](https://prettier.io) formats the whole repo. Run from the repo root:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install # once, to install Prettier
|
||||||
|
npm run format # write
|
||||||
|
npm run format:check # verify (also run in CI)
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI / Deployment
|
||||||
|
|
||||||
|
Forgejo Actions workflows in `.forgejo/workflows/`:
|
||||||
|
|
||||||
|
- `test.yml` — formatting check + backend tests on every push/PR.
|
||||||
|
- `docker-publish.yml` — builds and publishes the backend image from `backend/`.
|
||||||
|
- `deploy.yml` — deploys `frontend/` to Pages.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
node_modules
|
node_modules
|
||||||
Dockerfile
|
Dockerfile
|
||||||
.dockerignore
|
.dockerignore
|
||||||
|
*.test.js
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node server.js",
|
"start": "node server.js",
|
||||||
"dev": "nodemon server.js"
|
"dev": "nodemon server.js",
|
||||||
|
"test": "node --test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
|
|
|
||||||
8186
frontend/fizika.json
8186
frontend/fizika.json
File diff suppressed because it is too large
Load diff
31
package-lock.json
generated
Normal file
31
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"name": "fizika",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "fizika",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^3.4.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
|
||||||
|
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
package.json
Normal file
13
package.json
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "fizika",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Fizika emelt szintű érettségi gyakorló alkalmazás (repo tooling).",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"format:check": "prettier --check ."
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^3.4.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue