From e921b49e0cd285219a9fc034fdc7ac05af91b9ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Apr 2026 20:51:58 +0000 Subject: [PATCH 1/2] audit: fix credentials, generalize templates, add open-source readiness checklist Agent-Logs-Url: https://github.com/it-amanuens/rodatraden/sessions/2e8fc556-e14e-49bf-b3fa-161d46d0a43d Co-authored-by: SpiderQubit <25958778+SpiderQubit@users.noreply.github.com> --- DOCKER_README.md | 8 +- OPEN_SOURCE_CHECKLIST.md | 262 ++++++++++++++++++++++++++++++++++++ README.md | 1 - docker-compose-template.yml | 16 +-- tf/settings-template.py | 10 +- 5 files changed, 280 insertions(+), 17 deletions(-) create mode 100644 OPEN_SOURCE_CHECKLIST.md diff --git a/DOCKER_README.md b/DOCKER_README.md index 642510a..a9b567e 100644 --- a/DOCKER_README.md +++ b/DOCKER_README.md @@ -1,13 +1,17 @@ # Röda Tråden Production Setup Guide +> **Note:** This guide was originally written for a specific deployment at Teknisk Fysik, Umeå University +> (running at `rt.tekniskfysik.se` on a single Linux server). File paths such as `/root/3dlabbetwiki/` +> and service names like `3dlabbetwiki-reverse-proxy-1` are specific to that environment. +> Adapt paths, service names, hostnames, and Docker Compose file locations to match your own deployment. + ## Architecture Overview ``` Internet → Traefik (reverse proxy) → Docker containers ↓ ┌───────────────────────────────────────┐ - │ rt.tekniskfysik.se → rodatraden:8000 │ - │ 3dwiki.tekniskfysik.se → bookstack │ + │ → rodatraden:8000 │ └───────────────────────────────────────┘ ``` diff --git a/OPEN_SOURCE_CHECKLIST.md b/OPEN_SOURCE_CHECKLIST.md new file mode 100644 index 0000000..a29721d --- /dev/null +++ b/OPEN_SOURCE_CHECKLIST.md @@ -0,0 +1,262 @@ +# Open Source Readiness Checklist + +This document tracks everything that should be addressed before making the +repository fully public. Items are grouped by theme. Completed items are +checked off. + +--- + +## 🔐 Security & Credentials + +- [x] **Personal e-mail removed from `tf/settings-template.py`** + Replace `lucash@fastmail.com` → `admin@example.com`. + +- [x] **Hardcoded MariaDB passwords removed from `docker-compose-template.yml`** + Replace `MLNNpxjPbpjdiFCO3K6bOkL5W` / `Xav716CIwmjdsFd2wmPF8qvg3` → ``. + +- [x] **Let's Encrypt e-mail replaced in `docker-compose-template.yml`** + Replace `it@tekniskfysik.se` → `your-email@example.com`. + +- [ ] **Font Awesome kit ID in `base.html`** + `https://kit.fontawesome.com/c6e9b99eeb.js` embeds a kit ID tied to a specific + Font Awesome account. Anyone forking the project will use your quota and you + could block their domains at any time. + **Options:** + - Self-host Font Awesome (free tier, no CDN account needed), or + - Document in README that users must create their own FA kit and substitute the URL. + + ```html + + + ``` + +--- + +## 🏷️ TF-Specific Naming & Content + +- [ ] **`tf/` Django project directory** + The project configuration directory is named `tf` (short for "Teknisk Fysik"). + For a generic open-source project this should be renamed, e.g. to `config/` or + `rodatraden_project/`. + + This rename affects: + - `manage.py` → `os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tf.settings')` + - `tf/wsgi.py` → `os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tf.settings')` + - `tf/settings-template.py` → `ROOT_URLCONF = 'tf.urls'`, `WSGI_APPLICATION = 'tf.wsgi.application'` + - `Dockerfile` → `CMD ["gunicorn", ..., "tf.wsgi:application"]` + - Any deployment guide or environment variable (`DJANGO_SETTINGS_MODULE`) + + **Example rename (bash):** + ```bash + mv tf config + # Then update every reference to 'tf.' in Python files: + find . -name "*.py" -exec sed -i 's/\btf\.\(settings\|urls\|wsgi\)\b/config.\1/g' {} + + ``` + +- [ ] **Hardcoded "Teknisk Fysik i Umeå" text in templates** + + Several templates contain content that is specific to the original programme: + + | File | Line | Content | + |------|------|---------| + | `rodatraden/templates/rodatraden/index.html` | 19 | "…kurserna på Teknisk fysik i Umeå" | + | `rodatraden/templates/rodatraden/profile/profile_list.html` | 24 | "Som student på Teknisk Fysik i Umeå…" | + | `rodatraden/templates/rodatraden/profile/profile_detail.html` | 128 | "Forsknings- och utvecklingsprojekt inom teknisk fysik" (hardcoded course name) | + + **Suggested fix:** Move these strings to a site-wide settings variable (e.g. + `SITE_PROGRAMME_NAME`) configured in `settings.py`, or make the texts generic + ("your programme" / "ditt program"). + +- [ ] **Hardcoded UMU video link in `block_detail.html`** + ```html + + + ``` + This link points to an internal server at Umeå University that other deployments + won't have. Either remove the link, make it configurable via settings, or + replace with a generic help text. + +- [ ] **`Exam` model docstring** + ```python + # rodatraden/models.py, line 753 + class Exam(models.Model): + """Exams such as teknisk fysik.""" + ``` + Update to a generic description, e.g. `"""Degree/exam definitions."""`. + +- [ ] **Comment in `forms.py`** + ```python + # forms.py, line 285 + # (probably only TF) + ``` + Either remove the comment or expand it to explain why `Exam.objects.first()` + is used as default—for a new installation this will return `None`. + +--- + +## 🛠️ Setup From Scratch / Onboarding + +- [ ] **Migrations excluded from version control** + `.gitignore` excludes `rodatraden/migrations/0*_*.py`, meaning a fresh clone has + **no migration files**. The README already mentions `makemigrations`, but this is + non-standard for an open-source Django project. Consider either: + - Committing initial migrations (common practice), or + - Adding a note in the README clearly explaining that `makemigrations` *must* be + run before `migrate` on a fresh clone (the current instructions already mention + this but it is easy to miss). + +- [ ] **Required initial data has no fixture or seed script** + After a fresh `migrate`, the database is empty. The admin panel requires manually + creating: + - **Institutioner** (Departments) + - **Nivåer** (Levels, e.g. "Grundläggande" / "Avancerat") + - **Tidsperioder** (Time periods — the week offsets for each läsperiod) + - **Akademiska år** (Academic years, if `AcademicYear` model is still in use) + - **Spår** / **Profiler** (Tracks / Profiles) + + More critically, `Course` has hard-coded `default=1` for `department` and + `level`: + ```python + # models.py + department = models.ForeignKey(Department, ..., default=1) + level = models.ForeignKey(Level, ..., default=1) + ``` + If the first migrated DB doesn't have `Department.id=1` or `Level.id=1`, course + creation will fail with an `IntegrityError`. + + **Recommended fix:** Provide an `initial_data.json` fixture (or a + `create_initial_data` management command) that seeds the required records, and + remove or change the hard-coded `default=1`. + + ```bash + # Once you have the desired initial data in the DB, export it: + python manage.py dumpdata rodatraden.Department rodatraden.Level \ + rodatraden.TimePeriod --indent 2 -o rodatraden/fixtures/initial_data.json + # Then load with: + python manage.py loaddata initial_data + ``` + +- [ ] **`Dockerfile` runs `collectstatic` at build time without settings** + ```dockerfile + RUN python manage.py collectstatic --noinput + ``` + This works when a `tf/settings.py` is present in the image (e.g. baked in or + mounted). If the settings file is supplied via environment variable at runtime, + the build step will fail. Consider deferring `collectstatic` to the `CMD` + entrypoint or a startup script. + +- [ ] **`requirements.txt` is unpinned** + All packages use floating versions (`django`, `Pillow`, etc.). This means + `pip install -r requirements.txt` will install whatever is newest at that moment, + which can break the build silently over time. + **Fix:** Pin versions with `pip freeze > requirements-lock.txt` and use that for + production installs: + ```bash + pip freeze | grep -v "^-e" > requirements-lock.txt + ``` + +--- + +## 📝 Documentation & README + +- [x] **Duplicate `#### Windows` heading in README.md** — removed. + +- [x] **`DOCKER_README.md` contains server-specific paths** — added disclaimer note. + +- [ ] **README still references Django 2.2 documentation links** + Multiple links point to `https://docs.djangoproject.com/en/2.2/…`. Update to + `https://docs.djangoproject.com/en/stable/…`. + +- [ ] **README "Clean install" section uses Swedish admin labels without translation** + The section mentions "akademiska år", "institutioner", "Nivåer" etc. without + explaining what each field means functionally. Add a short English note for + non-Swedish contributors. + +- [ ] **README "About Django" section says "Teknisk Fysik is more familiar with Python"** + Line 10: generalise to remove the programme-specific reasoning. + +- [ ] **`DOCKER_README.md` internal paths and service names** + Paths such as `/root/3dlabbetwiki/`, log lines referencing + `3dlabbetwiki-reverse-proxy-1`, and the command `cd /root/3dlabbetwiki` are + specific to the original server. These should be replaced with generic placeholders. + +--- + +## 🐛 Code Quality / Pending TODOs + +- [ ] **`urls.py` TODO — `` on user routes** + ```python + # rodatraden/urls.py, line 135 + # TODO: The is only to avoid complaints by the generic view. This + # should be removed, but I can't be arsed right now. Double security perhaps? + path('anvandare///andra', ...), + ``` + The `` in the URL is redundant (the view looks up by `username`), and + the comment is not suitable for a public repo. Either clean up the URL pattern + or document *why* the pk is kept. + +- [ ] **`views.py` XXX — confusing `category_sum` vs `categories_sum`** + ```python + # rodatraden/views.py, line 1375 + # XXX: The variables name will be confusingly similar until I figure out how category_sum works. + categories_sum = [float(sum) for sum in category_sum.values()] + ``` + Rename one variable to resolve the confusion, or document the distinction. + +- [ ] **`views.py` XXX — `enable=0` / `enable=false` bug** + ```python + # rodatraden/views.py, line 1726 + # XXX: Will also be true for "enable=0" or "enable=false" etc. + if (shouldEnable): + block.should_verify_prerequisites = True + ``` + Any non-empty string (including `"0"` or `"false"`) will enable the flag. Fix + with an explicit string comparison: + ```python + block.should_verify_prerequisites = shouldEnable.lower() in ('1', 'true', 'yes') + ``` + +- [ ] **`models.py` XXX — `Profile` doubles as block schedule view settings** + ```python + # rodatraden/models.py, line 238 + # XXX: The base block should be its own model, and not have to be a profile + # piggybacking of the view settings. + ``` + This is a design debt item. Document it as a known issue or open a separate + issue for it before going public. + +- [ ] **`profile_detail.html` hardcoded content with draft marker** + ```html + {# Hardcoded for now, without links. This is a first draft. #} + ``` + The "Projekt och Teknik för hållbar utveckling" section is hardcoded with + TF-specific course names. Either remove it, make it database-driven, or replace + with a generic placeholder. + +- [ ] **Typo in `views.py`: `prinvate_courses_json`** + ```python + # rodatraden/views.py, line 1379 + prinvate_courses_json = [course.as_json() for course in block.privatecourses.all()] + ``` + Should be `private_courses_json`. + +--- + +## 🌐 Internationalisation + +- [ ] **Language is hard-coded to Swedish** + `settings-template.py` has `LANGUAGE_CODE = 'sv-se'` and all UI text is in + Swedish. For an open-source tool usable by other universities this could be + a barrier. At minimum, document this as a known limitation and explain how + to change the language code. + +--- + +## 🚀 Nice-to-Have Before Public Release + +- [ ] Add a `CONTRIBUTING.md` explaining how to set up a local dev environment + (venv, DB, initial data, superuser) in a single place. +- [ ] Add a `LICENSE` file if one is not already present. +- [ ] Consider adding a `.env.example` file as the canonical reference for all + environment variables needed to run the project. +- [ ] Run `python manage.py check --deploy` and address any warnings. diff --git a/README.md b/README.md index 682283c..51202d3 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,6 @@ For windows see section lower down give more detail than that. (one more detail, uncomment wfastcgi in requirements.txt on windows) - #### Windows #### Windows I assume that you already have a Windows Server with IIS enabled (including the CGI role service) and Python installed system-wide (for example `C:\Python312`). This guide focuses on deployment using IIS and FastCGI (`wfastcgi`). diff --git a/docker-compose-template.yml b/docker-compose-template.yml index 8792435..f6c6743 100644 --- a/docker-compose-template.yml +++ b/docker-compose-template.yml @@ -14,7 +14,7 @@ services: - --entrypoints.websecure.forwardedheaders.insecure=true - --certificatesresolvers.myresolver.acme.tlschallenge=true - - --certificatesresolvers.myresolver.acme.email=it@tekniskfysik.se + - --certificatesresolvers.myresolver.acme.email=your-email@example.com - --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json - --log.level=DEBUG ports: @@ -42,10 +42,10 @@ services: - PUID=1000 - PGID=1000 - TZ=Etc/UTC - - MYSQL_ROOT_PASSWORD=MLNNpxjPbpjdiFCO3K6bOkL5W + - MYSQL_ROOT_PASSWORD= - MYSQL_DATABASE=bookstack - MYSQL_USER=bookstack - - MYSQL_PASSWORD=Xav716CIwmjdsFd2wmPF8qvg3 + - MYSQL_PASSWORD= volumes: - ./bookstack_db_data:/config labels: @@ -53,17 +53,17 @@ services: restart: unless-stopped rodatraden: - build: /root/rodatraden + build: . container_name: rodatraden environment: - TZ=Europe/Stockholm volumes: - - /root/rodatraden:/app - - /root/rodatraden/static:/app/static - - /root/rodatraden/media:/app/media + - .:/app + - ./static:/app/static + - ./media:/app/media labels: - "traefik.enable=true" - - "traefik.http.routers.rodatraden.rule=Host(`rt.tekniskfysik.se`)" + - "traefik.http.routers.rodatraden.rule=Host(`your.domain.example.com`)" - "traefik.http.routers.rodatraden.entrypoints=websecure" - "traefik.http.routers.rodatraden.tls=true" - "traefik.http.routers.rodatraden.tls.certresolver=myresolver" diff --git a/tf/settings-template.py b/tf/settings-template.py index 7ced5df..403c230 100644 --- a/tf/settings-template.py +++ b/tf/settings-template.py @@ -1,13 +1,11 @@ """ -Django settings for tf project. - -Generated by 'django-admin startproject' using Django 2.2.1. +Django settings for the project. For more information on this file, see -https://docs.djangoproject.com/en/2.2/topics/settings/ +https://docs.djangoproject.com/en/stable/topics/settings/ For the full list of settings and their values, see -https://docs.djangoproject.com/en/2.2/ref/settings/ +https://docs.djangoproject.com/en/stable/ref/settings/ """ import os @@ -108,7 +106,7 @@ DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' # Email -DEFAULT_FROM_EMAIL = 'lucash@fastmail.com' +DEFAULT_FROM_EMAIL = 'admin@example.com' # Temporary for development purposes # EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend" From cd2991aa0c1efb1f9981b8e2d1b561065d20f7fb Mon Sep 17 00:00:00 2001 From: it-amanuens <64007851+it-amanuens@users.noreply.github.com> Date: Thu, 30 Apr 2026 11:05:48 +0200 Subject: [PATCH 2/2] Delete docker-compose-template.yml Already updated in master --- docker-compose-template.yml | 73 ------------------------------------- 1 file changed, 73 deletions(-) delete mode 100644 docker-compose-template.yml diff --git a/docker-compose-template.yml b/docker-compose-template.yml deleted file mode 100644 index f6c6743..0000000 --- a/docker-compose-template.yml +++ /dev/null @@ -1,73 +0,0 @@ -services: - reverse-proxy: - image: traefik:v3.6 - command: - - --api.insecure=true - - --providers.docker - - --providers.docker.endpoint=unix:///var/run/docker.sock - - --entrypoints.web.address=:80 - - --entrypoints.web.http.redirections.entrypoint.to=websecure - - --entrypoints.web.http.redirections.entrypoint.scheme=https - - --entrypoints.websecure.address=:443 - # forward ip address - - --entrypoints.web.forwardedheaders.insecure=true - - --entrypoints.websecure.forwardedheaders.insecure=true - - - --certificatesresolvers.myresolver.acme.tlschallenge=true - - --certificatesresolvers.myresolver.acme.email=your-email@example.com - - --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json - - --log.level=DEBUG - ports: - - "80:80" - - "443:443" - - "8080:8080" - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ./letsencrypt:/letsencrypt - labels: - # HTTP to HTTPS redirection middleware - - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" - # Catchall router for HTTP - - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)" - - "traefik.http.routers.http-catchall.entrypoints=web" - - "traefik.http.routers.http-catchall.middlewares=redirect-to-https" - - "traefik.http.routers.http-catchall.service=noop@internal" - - "traefik.http.routers.http-catchall.priority=1" - restart: unless-stopped - - mariadb: - image: lscr.io/linuxserver/mariadb:11.4.4 - container_name: mariadb - environment: - - PUID=1000 - - PGID=1000 - - TZ=Etc/UTC - - MYSQL_ROOT_PASSWORD= - - MYSQL_DATABASE=bookstack - - MYSQL_USER=bookstack - - MYSQL_PASSWORD= - volumes: - - ./bookstack_db_data:/config - labels: - - "traefik.enable=false" # Don't expose MariaDB - restart: unless-stopped - - rodatraden: - build: . - container_name: rodatraden - environment: - - TZ=Europe/Stockholm - volumes: - - .:/app - - ./static:/app/static - - ./media:/app/media - labels: - - "traefik.enable=true" - - "traefik.http.routers.rodatraden.rule=Host(`your.domain.example.com`)" - - "traefik.http.routers.rodatraden.entrypoints=websecure" - - "traefik.http.routers.rodatraden.tls=true" - - "traefik.http.routers.rodatraden.tls.certresolver=myresolver" - - "traefik.http.services.rodatraden.loadbalancer.server.port=8000" - - "traefik.http.middlewares.rodatraden-headers.headers.customrequestheaders.X-Forwarded-Proto=https" - - "traefik.http.routers.rodatraden.middlewares=rodatraden-headers" - restart: unless-stopped