diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a2128df --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +# --- Frontend Build --- +FROM node:20-alpine as frontend-builder +WORKDIR /app/frontend +COPY frontend/package*.json ./ +RUN npm install --force +COPY frontend/ ./ +RUN npm run build + +# --- Backend Build --- +FROM rust:1.77-slim as backend-builder +WORKDIR /app/backend +# Pre-build dependencies for caching +COPY backend/Cargo.toml backend/Cargo.lock ./ +RUN mkdir src && echo "fn main() {}" > src/main.rs && cargo build --release && rm -rf src +COPY backend/src ./src +COPY backend/migrations ./migrations +RUN cargo build --release + +# --- Final Image --- +FROM debian:bookworm-slim +RUN apt-get update && apt-get install -y sqlite3 ca-certificates && rm -rf /var/lib/apt/lists/* +WORKDIR /app +COPY --from=backend-builder /app/backend/target/release/attendance ./server +COPY --from=frontend-builder /app/frontend/build ./frontend/build + +ENV STATIC_DIR=/app/frontend/build +ENV DATABASE_URL=sqlite:/data/attendance.db +EXPOSE 3000 +CMD ["./server"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1c13dd1 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +.PHONY: dev dev-backend dev-frontend build test compose-up + +dev: + @echo "Starting backend and frontend in parallel..." + @make -j 2 dev-backend dev-frontend + +dev-backend: + cd backend && cargo run + +dev-frontend: + cd frontend && npm run dev + +build: + cd frontend && npm run build + cd backend && cargo build --release + +test: + cd backend && cargo test + +compose-up: + docker-compose up --build diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..66eb1aa --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.8' + +services: + app: + build: . + ports: + - "3000:3000" + volumes: + - ./data:/data + environment: + - DATABASE_URL=sqlite:/data/attendance.db + - STATIC_DIR=/app/frontend/build + restart: always diff --git a/k8s/cronjob.yaml b/k8s/cronjob.yaml new file mode 100644 index 0000000..3597653 --- /dev/null +++ b/k8s/cronjob.yaml @@ -0,0 +1,28 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: tutortool-backup +spec: + schedule: "0 3 * * *" + jobTemplate: + spec: + template: + spec: + containers: + - name: backup + image: alpine:latest + command: + - /bin/sh + - -c + - | + apk add --no-cache sqlite + sqlite3 /data/attendance.db "VACUUM INTO '/data/backup-$(date +%F).sqlite'" + find /data -name "backup-*.sqlite" -mtime +7 -delete + volumeMounts: + - name: db-storage + mountPath: /data + restartPolicy: OnFailure + volumes: + - name: db-storage + persistentVolumeClaim: + claimName: tutortool-pvc diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml new file mode 100644 index 0000000..dbbf9be --- /dev/null +++ b/k8s/deployment.yaml @@ -0,0 +1,59 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tutortool +spec: + replicas: 1 + selector: + matchLabels: + app: tutortool + template: + metadata: + labels: + app: tutortool + spec: + containers: + - name: app + image: tutor-registry:latest + ports: + - containerPort: 3000 + env: + - name: DATABASE_URL + value: "sqlite:/data/attendance.db" + - name: STATIC_DIR + value: "/app/frontend/build" + volumeMounts: + - name: db-storage + mountPath: /data + volumes: + - name: db-storage + persistentVolumeClaim: + claimName: tutortool-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: tutortool +spec: + selector: + app: tutortool + ports: + - port: 80 + targetPort: 3000 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: tutortool-ingress +spec: + rules: + - host: tutor.puchstein.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: tutortool + port: + number: 80 diff --git a/k8s/pvc.yaml b/k8s/pvc.yaml new file mode 100644 index 0000000..12af263 --- /dev/null +++ b/k8s/pvc.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: tutortool-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi