chore: clean up repo state and improve dev tooling
- Add IF NOT EXISTS to all migration DDL for idempotency - Support PORT env var in backend (for compose flexibility) - Support HOST_PORT env var in docker-compose - Improve seed-demo to apply migrations before seeding - Gitignore .claude/ session cache
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
.gemini
|
||||
.claude/
|
||||
.worktrees/
|
||||
docs/
|
||||
|
||||
|
||||
11
Makefile
11
Makefile
@@ -21,5 +21,12 @@ compose-up:
|
||||
docker-compose up --build
|
||||
|
||||
seed-demo:
|
||||
@echo "Seeding demo data..."
|
||||
sqlite3 $${DATABASE_URL#sqlite:} < backend/demo/demo_seed.sql
|
||||
@mkdir -p data
|
||||
@echo "Applying migrations and seeding demo data..."
|
||||
@DB_PATH=$${DATABASE_URL:-sqlite:data/attendance.db}; \
|
||||
DB_FILE=$${DB_PATH#sqlite:}; \
|
||||
for f in backend/migrations/*.sql; do \
|
||||
echo "Applying $$f..."; \
|
||||
sqlite3 $$DB_FILE < $$f; \
|
||||
done; \
|
||||
sqlite3 $$DB_FILE < backend/demo/demo_seed.sql
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
CREATE TABLE courses (
|
||||
CREATE TABLE IF NOT EXISTS courses (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
semester TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE tutors (
|
||||
CREATE TABLE IF NOT EXISTS tutors (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
password_hash TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE tutor_courses (
|
||||
CREATE TABLE IF NOT EXISTS tutor_courses (
|
||||
tutor_id INTEGER NOT NULL REFERENCES tutors(id),
|
||||
course_id INTEGER NOT NULL REFERENCES courses(id),
|
||||
PRIMARY KEY (tutor_id, course_id)
|
||||
);
|
||||
|
||||
CREATE TABLE students (
|
||||
CREATE TABLE IF NOT EXISTS students (
|
||||
id INTEGER PRIMARY KEY,
|
||||
course_id INTEGER NOT NULL REFERENCES courses(id),
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE rooms (
|
||||
CREATE TABLE IF NOT EXISTS rooms (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
layout_json TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE sessions (
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id INTEGER PRIMARY KEY,
|
||||
course_id INTEGER NOT NULL REFERENCES courses(id),
|
||||
week_nr INTEGER NOT NULL,
|
||||
@@ -37,7 +37,7 @@ CREATE TABLE sessions (
|
||||
UNIQUE(course_id, week_nr)
|
||||
);
|
||||
|
||||
CREATE TABLE slots (
|
||||
CREATE TABLE IF NOT EXISTS slots (
|
||||
id INTEGER PRIMARY KEY,
|
||||
session_id INTEGER NOT NULL REFERENCES sessions(id),
|
||||
room_id INTEGER REFERENCES rooms(id),
|
||||
@@ -48,7 +48,7 @@ CREATE TABLE slots (
|
||||
code TEXT UNIQUE
|
||||
);
|
||||
|
||||
CREATE TABLE attendances (
|
||||
CREATE TABLE IF NOT EXISTS attendances (
|
||||
id INTEGER PRIMARY KEY,
|
||||
slot_id INTEGER NOT NULL REFERENCES slots(id),
|
||||
student_id INTEGER NOT NULL REFERENCES students(id),
|
||||
@@ -58,7 +58,7 @@ CREATE TABLE attendances (
|
||||
UNIQUE(slot_id, seat_id)
|
||||
);
|
||||
|
||||
CREATE TABLE notes (
|
||||
CREATE TABLE IF NOT EXISTS notes (
|
||||
id INTEGER PRIMARY KEY,
|
||||
slot_id INTEGER NOT NULL REFERENCES slots(id),
|
||||
student_id INTEGER NOT NULL REFERENCES students(id),
|
||||
@@ -69,11 +69,11 @@ CREATE TABLE notes (
|
||||
);
|
||||
|
||||
-- Indexes on high-frequency FK columns (SQLite does not auto-index FKs)
|
||||
CREATE INDEX idx_students_course ON students(course_id);
|
||||
CREATE INDEX idx_sessions_course ON sessions(course_id);
|
||||
CREATE INDEX idx_slots_session ON slots(session_id);
|
||||
CREATE INDEX idx_slots_tutor ON slots(tutor_id);
|
||||
CREATE INDEX idx_attendances_slot ON attendances(slot_id);
|
||||
CREATE INDEX idx_attendances_student ON attendances(student_id);
|
||||
CREATE INDEX idx_notes_slot ON notes(slot_id);
|
||||
CREATE INDEX idx_notes_student ON notes(student_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_students_course ON students(course_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_course ON sessions(course_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_slots_session ON slots(session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_slots_tutor ON slots(tutor_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_attendances_slot ON attendances(slot_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_attendances_student ON attendances(student_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_notes_slot ON notes(slot_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_notes_student ON notes(student_id);
|
||||
|
||||
@@ -28,8 +28,12 @@ async fn main() {
|
||||
.fallback(ServeFile::new(format!("{static_dir}/index.html")))
|
||||
);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await
|
||||
.expect("failed to bind :3000");
|
||||
tracing::info!("listening on :3000");
|
||||
let port = std::env::var("PORT")
|
||||
.unwrap_or_else(|_| "3000".into());
|
||||
let addr = format!("0.0.0.0:{port}");
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(&addr).await
|
||||
.expect("failed to bind");
|
||||
tracing::info!("listening on :{}", port);
|
||||
axum::serve(listener, app).await.expect("server error");
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@ services:
|
||||
app:
|
||||
build: .
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "${HOST_PORT:-3000}:3000"
|
||||
volumes:
|
||||
- ./data:/data
|
||||
environment:
|
||||
- DATABASE_URL=sqlite:/data/attendance.db
|
||||
- STATIC_DIR=/app/frontend/build
|
||||
- JWT_SECRET=${JWT_SECRET:-dev_secret_for_demo}
|
||||
- PORT=3000
|
||||
restart: always
|
||||
|
||||
Reference in New Issue
Block a user