Compare commits
1 Commits
upgrade/dj
...
feature/im
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ba05a2913 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,5 +16,4 @@ AGENT*.md
|
|||||||
# Diagram cache directory
|
# Diagram cache directory
|
||||||
media/diagram_cache/
|
media/diagram_cache/
|
||||||
.env
|
.env
|
||||||
data/
|
data/db.sqlite3
|
||||||
dataremote/
|
|
||||||
|
|||||||
141
VorgabenUI/settings-docker.py
Normal file
141
VorgabenUI/settings-docker.py
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
"""
|
||||||
|
Django settings for VorgabenUI project.
|
||||||
|
|
||||||
|
Generated by 'django-admin startproject' using Django 5.2.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/5.2/topics/settings/
|
||||||
|
|
||||||
|
For the full list of settings and their values, see
|
||||||
|
https://docs.djangoproject.com/en/5.2/ref/settings/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = os.environ.get("SECRET_KEY")
|
||||||
|
|
||||||
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
|
DEBUG = bool(os.environ.get("DEBUG", default=0))
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS","127.0.0.1").split(",")
|
||||||
|
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
'dokumente',
|
||||||
|
'abschnitte',
|
||||||
|
'stichworte',
|
||||||
|
'mptt',
|
||||||
|
'nested_admin',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'VorgabenUI.urls'
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'VorgabenUI.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': BASE_DIR / 'data/db.sqlite3',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Password validation
|
||||||
|
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/5.2/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'de-ch'
|
||||||
|
|
||||||
|
TIME_ZONE = 'UTC'
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
|
||||||
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
# https://docs.djangoproject.com/en/5.2/howto/static-files/
|
||||||
|
|
||||||
|
STATIC_URL = '/static/'
|
||||||
|
STATIC_ROOT="/home/adebaumann/VorgabenUI/staticfiles/"
|
||||||
|
STATICFILES_DIRS= (
|
||||||
|
os.path.join(BASE_DIR,"static"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Media files (User-uploaded content)
|
||||||
|
MEDIA_URL = '/media/'
|
||||||
|
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||||
|
|
||||||
|
# Diagram cache settings
|
||||||
|
DIAGRAM_CACHE_DIR = 'diagram_cache' # relative to MEDIA_ROOT
|
||||||
|
|
||||||
|
# Default primary key field type
|
||||||
|
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
DATA_UPLOAD_MAX_NUMBER_FIELDS=10250
|
||||||
|
NESTED_ADMIN_LAZY_INLINES = True
|
||||||
@@ -138,7 +138,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
|
|
||||||
LANGUAGE_CODE = 'de-ch'
|
LANGUAGE_CODE = 'de-ch'
|
||||||
|
|
||||||
TIME_ZONE = 'Europe/Zurich'
|
TIME_ZONE = 'UTC'
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
|
|||||||
@@ -14,21 +14,19 @@ spec:
|
|||||||
app: django
|
app: django
|
||||||
spec:
|
spec:
|
||||||
securityContext:
|
securityContext:
|
||||||
fsGroup: 99
|
fsGroup: 999
|
||||||
fsGroupChangePolicy: "OnRootMismatch"
|
fsGroupChangePolicy: "OnRootMismatch"
|
||||||
initContainers:
|
initContainers:
|
||||||
- name: loader
|
- name: loader
|
||||||
image: git.baumann.gr/adebaumann/vui-data-loader:0.11
|
image: git.baumann.gr/adebaumann/vui-data-loader:0.11
|
||||||
command: [ "sh","-c","if [ ! -f /data/db.sqlite3 ] || [ ! -s /data/db.sqlite3 ]; then cp preload/preload.sqlite3 /data/db.sqlite3 && echo 'Database copied from preload'; else echo 'Existing database preserved'; fi" ]
|
command: [ "sh","-c","cp -n preload/preload.sqlite3 /data/db.sqlite3; chown -R 999:999 /data; ls -la /data; sleep 10; exit 0" ]
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: data
|
- name: data
|
||||||
mountPath: /data
|
mountPath: /data
|
||||||
containers:
|
containers:
|
||||||
- name: web
|
- name: web
|
||||||
image: git.baumann.gr/adebaumann/vui:0.983
|
image: git.baumann.gr/adebaumann/vui:0.980
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
securityContext:
|
|
||||||
runAsUser: 99
|
|
||||||
env:
|
env:
|
||||||
# Secret configuration
|
# Secret configuration
|
||||||
- name: VORGABENUI_SECRET
|
- name: VORGABENUI_SECRET
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ metadata:
|
|||||||
name: django-data-pv
|
name: django-data-pv
|
||||||
namespace: vorgabenui
|
namespace: vorgabenui
|
||||||
spec:
|
spec:
|
||||||
claimRef:
|
|
||||||
name: django-data-pvc
|
|
||||||
namespace: vorgabenui
|
|
||||||
capacity:
|
capacity:
|
||||||
storage: 2Gi
|
storage: 2Gi
|
||||||
accessModes:
|
accessModes:
|
||||||
|
|||||||
512
code-review.md
512
code-review.md
@@ -1,512 +0,0 @@
|
|||||||
# Code Review: vgui-cicd Django Project
|
|
||||||
|
|
||||||
**Date:** January 20, 2026
|
|
||||||
**Reviewer:** AI Code Review
|
|
||||||
**Project Path:** /home/adebaumann/development/vgui-cicd
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. PROJECT STRUCTURE
|
|
||||||
|
|
||||||
### Overview
|
|
||||||
The project follows Django conventions with a clear app structure:
|
|
||||||
- **VorgabenUI** - Main project settings, URLs, WSGI/ASGI
|
|
||||||
- **dokumente** - Core document and Vorgabe models (315 lines)
|
|
||||||
- **abschnitte** - Text section models and utilities
|
|
||||||
- **stichworte** - Keyword/stichwort models
|
|
||||||
- **referenzen** - Reference models (MPTT-based)
|
|
||||||
- **rollen** - Role models
|
|
||||||
- **pages** - General pages and views
|
|
||||||
- **diagramm_proxy** - Diagram caching functionality
|
|
||||||
|
|
||||||
### Issues Found
|
|
||||||
|
|
||||||
**Minor - settings-docker.py**: The `settings-docker.py` file has duplicate `AUTH_PASSWORD_VALIDATORS` definitions (lines 92-105 and 183-199), which is redundant.
|
|
||||||
- **File**: `/home/adebaumann/development/vgui-cicd/VorgabenUI/settings-docker.py` (lines 92-105 and 183-199)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. SETTINGS REVIEW
|
|
||||||
|
|
||||||
### Critical Issues
|
|
||||||
|
|
||||||
**1. Fallback SECRET_KEY in production** (`/home/adebaumann/development/vgui-cicd/VorgabenUI/settings.py`, lines 27-47)
|
|
||||||
```python
|
|
||||||
SECRET_KEY = os.environ.get('VORGABENUI_SECRET')
|
|
||||||
if not SECRET_KEY:
|
|
||||||
is_build_env = any([...])
|
|
||||||
debug_mode = ...
|
|
||||||
if debug_mode or is_build_env:
|
|
||||||
SECRET_KEY = 'dev-fallback-key-for-local-debugging-only-not-for-production-use-12345'
|
|
||||||
```
|
|
||||||
- **Issue**: Even though there's a check for build environments, the hardcoded fallback key creates a significant security risk if the environment variable is not properly set in production
|
|
||||||
- **Recommendation**: The fallback should NEVER be enabled, even in development - require the environment variable to be set
|
|
||||||
|
|
||||||
**2. DEBUG mode default** (`/home/adebaumann/development/vgui-cicd/VorgabenUI/settings.py`, line 24)
|
|
||||||
```python
|
|
||||||
DEBUG = os.environ.get('DEBUG', 'True').lower() in ('true', '1', 'yes', 'on')
|
|
||||||
```
|
|
||||||
- **Issue**: DEBUG defaults to True, which could expose sensitive information if environment variables are misconfigured
|
|
||||||
- **Recommendation**: Require explicit setting of DEBUG to False in production
|
|
||||||
|
|
||||||
### Major Issues
|
|
||||||
|
|
||||||
**3. ALLOWED_HOSTS with wildcard** (`/home/adebaumann/development/vgui-cicd/VorgabenUI/settings.py`, line 50)
|
|
||||||
```python
|
|
||||||
ALLOWED_HOSTS = os.environ.get('DJANGO_ALLOWED_HOSTS', "10.128.128.144,localhost,127.0.0.1,*").split(",")
|
|
||||||
```
|
|
||||||
- **Issue**: Default includes `*` which allows any host - dangerous in production
|
|
||||||
- **Recommendation**: Default should not include wildcard; require explicit configuration
|
|
||||||
|
|
||||||
**4. No rate limiting on authentication** (`/home/adebaumann/development/vgui-cicd/VorgabenUI/settings.py`)
|
|
||||||
- **Issue**: No login throttling or rate limiting configured for authentication endpoints
|
|
||||||
- **Recommendation**: Add `django-axes` or similar for brute-force protection
|
|
||||||
|
|
||||||
**5. SQLite in production** (`/home/adebaumann/development/vgui-cicd/VorgabenUI/settings.py`, lines 109-114)
|
|
||||||
```python
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
|
||||||
'NAME': BASE_DIR / 'data/db.sqlite3',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- **Issue**: SQLite is used as default database - not suitable for production with concurrent access
|
|
||||||
- **Recommendation**: Configure PostgreSQL or other production-ready database
|
|
||||||
|
|
||||||
### Minor Issues
|
|
||||||
|
|
||||||
**6. TIME_ZONE mismatch** (`/home/adebaumann/development/vgui-cicd/VorgabenUI/settings.py`, lines 139-145)
|
|
||||||
- **Issue**: `LANGUAGE_CODE = 'de-ch'` but `TIME_ZONE = 'UTC'` - timezone should probably be 'Europe/Zurich' for Swiss deployment
|
|
||||||
- **File**: `/home/adebaumann/development/vgui-cicd/VorgabenUI/settings.py` (lines 141)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. MODELS REVIEW
|
|
||||||
|
|
||||||
### Major Issues
|
|
||||||
|
|
||||||
**1. Missing `verbose_name` on models** - Several models are missing proper `verbose_name` in their Meta class:
|
|
||||||
- `Stichwort` (`/home/adebaumann/development/vgui-cicd/stichworte/models.py`, lines 4-11) - missing verbose_name
|
|
||||||
- `Referenz` (`/home/adebaumann/development/vgui-cicd/referenzen/models.py`, lines 6-27) - missing verbose_name
|
|
||||||
- `Rolle` (`/home/adebaumann/development/vgui-cicd/rollen/models.py`, lines 5-11) - missing verbose_name
|
|
||||||
- `Person` (`/home/adebaumann/development/vgui-cicd/dokumente/models.py`, lines 22-30) - missing verbose_name
|
|
||||||
- `Thema` (`/home/adebaumann/development/vgui-cicd/dokumente/models.py`, lines 32-39) - missing verbose_name
|
|
||||||
|
|
||||||
**2. BooleanField with `blank=True` but no default** (`/home/adebaumann/development/vgui-cicd/dokumente/models.py`, line 52)
|
|
||||||
```python
|
|
||||||
aktiv = models.BooleanField(blank=True)
|
|
||||||
```
|
|
||||||
- **Issue**: BooleanField should have explicit `default=False` for clarity
|
|
||||||
- **Recommendation**: Add `default=False`
|
|
||||||
|
|
||||||
**3. No database constraints for date validation** (`/home/adebaumann/development/vgui-cicd/dokumente/models.py`, lines 96-97)
|
|
||||||
```python
|
|
||||||
gueltigkeit_von = models.DateField()
|
|
||||||
gueltigkeit_bis = models.DateField(blank=True,null=True)
|
|
||||||
```
|
|
||||||
- **Issue**: No database-level constraint ensuring `gueltigkeit_bis >= gueltigkeit_von`
|
|
||||||
- **Recommendation**: Add constraint validation or override `save()` method
|
|
||||||
|
|
||||||
### Minor Issues
|
|
||||||
|
|
||||||
**4. Inconsistent naming in foreign key fields** - Some models use plural related names inconsistently:
|
|
||||||
- `autoren` and `pruefende` on `Dokument` use plural (correct)
|
|
||||||
- Could consider singular `related_name` for consistency where applicable
|
|
||||||
|
|
||||||
**5. Missing `related_name` on some ManyToMany fields** (`/home/adebaumann/development/vgui-cicd/dokumente/models.py`, line 289)
|
|
||||||
```python
|
|
||||||
autoren = models.ManyToManyField(Person) # Missing related_name
|
|
||||||
```
|
|
||||||
- **Recommendation**: Add `related_name='changelog_entries'` for clarity
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. VIEWS REVIEW
|
|
||||||
|
|
||||||
### Critical Issues
|
|
||||||
|
|
||||||
**1. No CSRF protection on comment endpoints** (`/home/adebaumann/development/vgui-cicd/dokumente/views.py`, lines 321-377)
|
|
||||||
```python
|
|
||||||
@require_POST
|
|
||||||
@login_required
|
|
||||||
def add_vorgabe_comment(request, vorgabe_id):
|
|
||||||
# No @csrf_exempt but also no CSRF token verification in the view
|
|
||||||
```
|
|
||||||
- **Issue**: The view uses `@require_POST` but doesn't verify CSRF tokens for the JSON endpoint
|
|
||||||
- **Recommendation**: Add `@csrf_exempt` ONLY if intentionally bypassing, or ensure CSRF is handled via the X-CSRFToken header (which is done in the template)
|
|
||||||
|
|
||||||
**Note**: Looking at line 368, the template sends `'X-CSRFToken': getCookie('csrftoken')`, so this is actually properly handled. **Not a bug.**
|
|
||||||
|
|
||||||
**2. XSS in comment display** (`/home/adebaumann/development/vgui-cicd/dokumente/views.py`, lines 305-306)
|
|
||||||
```python
|
|
||||||
escaped_text = escape(comment.text).replace('\n', '<br>')
|
|
||||||
```
|
|
||||||
- **Issue**: Using `escape()` is good, but line breaks are converted to `<br>` which could still be exploited
|
|
||||||
- **Note**: The `dangerous_patterns` check at lines 339-343 provides some protection
|
|
||||||
- **Recommendation**: Consider using a more robust HTML sanitization library
|
|
||||||
|
|
||||||
**3. No input validation on comment length** (`/home/adebaumann/development/vgui-cicd/dokumente/views.py`, line 335)
|
|
||||||
```python
|
|
||||||
if len(text) > 2000: # Reasonable length limit
|
|
||||||
```
|
|
||||||
- **Issue**: This is actually properly implemented with a 2000 character limit
|
|
||||||
- **Status**: OK
|
|
||||||
|
|
||||||
### Major Issues
|
|
||||||
|
|
||||||
**4. Referenz view lacks error handling** (`/home/adebaumann/development/vgui-cicd/referenzen/views.py`, lines 11-19)
|
|
||||||
```python
|
|
||||||
def detail(request, refid):
|
|
||||||
referenz_item = Referenz.objects.get(id=refid)
|
|
||||||
```
|
|
||||||
- **Issue**: `DoesNotExist` exception not caught - will return 500 error instead of 404
|
|
||||||
- **Recommendation**: Use `get_object_or_404` for consistency
|
|
||||||
|
|
||||||
### Minor Issues
|
|
||||||
|
|
||||||
**5. Search view allows complex regex patterns** (`/home/adebaumann/development/vgui-cicd/pages/views.py`, lines 36-70)
|
|
||||||
- **Issue**: The validation is good but the `groupby` usage at line 54 could fail if data is not properly sorted
|
|
||||||
- **Recommendation**: Add explicit ordering before groupby
|
|
||||||
|
|
||||||
**6. No rate limiting on search** (`/home/adebaumann/development/vgui-cicd/pages/views.py`)
|
|
||||||
- **Issue**: Search endpoint could be abused for DoS
|
|
||||||
- **Recommendation**: Add rate limiting
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. URL CONFIGURATION
|
|
||||||
|
|
||||||
### Minor Issues
|
|
||||||
|
|
||||||
**1. No URL namespace for include** (`/home/adebaumann/development/vgui-cicd/VorgabenUI/urls.py`, lines 31-35)
|
|
||||||
```python
|
|
||||||
path('dokumente/', include("dokumente.urls")),
|
|
||||||
path('stichworte/', include("stichworte.urls")),
|
|
||||||
```
|
|
||||||
- **Issue**: No `app_name` namespace defined in included apps
|
|
||||||
- **Recommendation**: Add `app_name = 'dokumente'` to `dokumente/urls.py` for cleaner reversals
|
|
||||||
|
|
||||||
**2. Inconsistent trailing slashes** - Most URLs have trailing slashes but not all - ensure consistency
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. TEMPLATES REVIEW
|
|
||||||
|
|
||||||
### Critical Issues
|
|
||||||
|
|
||||||
**1. XSS vulnerability in `standard_detail.html`** (`/home/adebaumann/development/vgui-cicd/dokumente/templates/standards/standard_detail.html`, lines 163-164)
|
|
||||||
```html
|
|
||||||
{% for ref in vorgabe.referenzpfade %}
|
|
||||||
{{ ref|safe }}{% if not forloop.last %}, {% endif %}
|
|
||||||
```
|
|
||||||
- **Issue**: Using `|safe` on reference paths could allow XSS if malicious content is stored
|
|
||||||
- **Recommendation**: Use `escape` filter and handle line breaks separately
|
|
||||||
|
|
||||||
**2. JavaScript in template without CSP** (`/home/adebaumann/development/vgui-cicd/dokumente/templates/standards/standard_detail.html`, lines 249-424)
|
|
||||||
- **Issue**: Inline JavaScript in template
|
|
||||||
- **Note**: CSP headers are set in the view (line 316-317), but inline scripts violate strict CSP
|
|
||||||
- **Recommendation**: Move JavaScript to external file
|
|
||||||
|
|
||||||
### Major Issues
|
|
||||||
|
|
||||||
**3. Missing ARIA labels and roles** - Several accessibility issues:
|
|
||||||
- Base template (`/home/adebaumann/development/vgui-cicd/pages/templates/base.html`) has navigation but missing `aria-label` on some elements
|
|
||||||
- The mobile navigation could use better ARIA attributes
|
|
||||||
|
|
||||||
**4. Missing alt attributes on images** (`/home/adebaumann/development/vgui-cicd/pages/templates/base.html`, lines 39-41)
|
|
||||||
```html
|
|
||||||
<img src="{% static 'swiss/img/logo-CH.svg' %}"
|
|
||||||
onerror="this.onerror=null; this.src='{% static 'swiss/img/logo-CH.png' %}'"
|
|
||||||
alt="Zur Startseite" />
|
|
||||||
```
|
|
||||||
- **Issue**: alt is present but could be more descriptive
|
|
||||||
- **Status**: Acceptable
|
|
||||||
|
|
||||||
### Minor Issues
|
|
||||||
|
|
||||||
**5. Hardcoded URLs in templates** (`/home/adebaumann/development/vgui-cicd/dokumente/templates/standards/incomplete_vorgaben.html`, line 21)
|
|
||||||
```html
|
|
||||||
<a href="/autorenumgebung/dokumente/vorgabe/{{ item.vorgabe.id }}/change/"
|
|
||||||
```
|
|
||||||
- **Issue**: Hardcoded admin URL instead of using URL reversal
|
|
||||||
- **Recommendation**: Use `{% url 'admin:dokumente_vorgabe_change' item.vorgabe.id %}`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. FORMS REVIEW
|
|
||||||
|
|
||||||
### Minor Issues
|
|
||||||
|
|
||||||
**1. No dedicated form classes** - Most form handling is done via Django admin forms or directly in views
|
|
||||||
- **Recommendation**: Consider creating explicit `ModelForm` classes for views that accept user input (e.g., comment form)
|
|
||||||
|
|
||||||
**2. VorgabeForm in admin could have more validation** (`/home/adebaumann/development/vgui-cicd/dokumente/admin.py`, lines 95-107)
|
|
||||||
- **Issue**: The form only validates Thema is required
|
|
||||||
- **Recommendation**: Add validation for date ranges and conflicts
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. MANAGEMENT COMMANDS
|
|
||||||
|
|
||||||
### Major Issues
|
|
||||||
|
|
||||||
**1. import-document command lacks error recovery** (`/home/adebaumann/development/vgui-cicd/dokumente/management/commands/import-document.py`, lines 288-349)
|
|
||||||
```python
|
|
||||||
for v in vorgaben_data:
|
|
||||||
try:
|
|
||||||
thema = Thema.objects.get(name=v["thema"])
|
|
||||||
except Thema.DoesNotExist:
|
|
||||||
self.stdout.write(self.style.WARNING(...))
|
|
||||||
continue # Silently skips vorgabe
|
|
||||||
```
|
|
||||||
- **Issue**: If one Vorgabe fails, the entire command may leave partial data
|
|
||||||
- **Recommendation**: Use `transaction.atomic()` to ensure atomicity
|
|
||||||
|
|
||||||
**2. No progress indicator for large imports** (`/home/adebaumann/development/vgui-cicd/dokumente/management/commands/import-document.py`)
|
|
||||||
- **Issue**: For large files, no progress shown
|
|
||||||
- **Recommendation**: Add progress output
|
|
||||||
|
|
||||||
### Minor Issues
|
|
||||||
|
|
||||||
**3. export_json command hardcodes "Standard IT-Sicherheit"** (`/home/adebaumann/development/vgui-cicd/dokumente/management/commands/export_json.py`, line 30)
|
|
||||||
```python
|
|
||||||
result = {
|
|
||||||
"Vorgabendokument": {
|
|
||||||
"Typ": "Standard IT-Sicherheit",
|
|
||||||
```
|
|
||||||
- **Issue**: Typ is hardcoded instead of using `dokument.dokumententyp.name`
|
|
||||||
- **Recommendation**: Use actual dokumententyp
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. TESTS REVIEW
|
|
||||||
|
|
||||||
### Major Issues
|
|
||||||
|
|
||||||
**1. No tests for diagram caching** - The `diagramm_proxy` module has no test coverage
|
|
||||||
- **Recommendation**: Add tests for `diagram_cache.py`
|
|
||||||
|
|
||||||
**2. No tests for referenzen views** - The tree view and detail view have no test coverage
|
|
||||||
- **Recommendation**: Add tests for `referenzen/views.py`
|
|
||||||
|
|
||||||
**3. No tests for authentication security** - Missing tests for:
|
|
||||||
- Brute-force protection
|
|
||||||
- Session management
|
|
||||||
- Password policy enforcement
|
|
||||||
|
|
||||||
### Minor Issues
|
|
||||||
|
|
||||||
**4. Test file organization** - `test_json.py` should be part of `tests.py` or in a proper test package structure
|
|
||||||
- **Status**: Acceptable but could be improved
|
|
||||||
|
|
||||||
**5. Tests rely on hardcoded paths** (`/home/adebaumann/development/vgui-cicd/dokumente/tests.py`, lines 1165-1168)
|
|
||||||
```python
|
|
||||||
self.assertContains(response, 'href="/autorenumgebung/dokumente/vorgabe/2/change/"')
|
|
||||||
```
|
|
||||||
- **Issue**: Uses hardcoded URL paths instead of URL reversal
|
|
||||||
- **Recommendation**: Use `reverse('admin:dokumente_vorgabe_change', args=[vorgabe.pk])`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. SECURITY REVIEW
|
|
||||||
|
|
||||||
### Critical Issues
|
|
||||||
|
|
||||||
**1. No rate limiting on any endpoint** - All views lack rate limiting
|
|
||||||
- **Recommendation**: Add `django-ratelimit` or similar
|
|
||||||
|
|
||||||
**2. Diagram cache potentially vulnerable to DoS** (`/home/adebaumann/development/vgui-cicd/diagramm_proxy/diagram_cache.py`, lines 24-67)
|
|
||||||
```python
|
|
||||||
def get_cached_diagram(diagram_type, diagram_content):
|
|
||||||
content_hash = compute_hash(diagram_content)
|
|
||||||
cache_path = get_cache_path(diagram_type, content_hash)
|
|
||||||
|
|
||||||
if default_storage.exists(cache_path):
|
|
||||||
return cache_path
|
|
||||||
|
|
||||||
# Generate diagram via POST request
|
|
||||||
url = f"{KROKI_UPSTREAM}/{diagram_type}/svg"
|
|
||||||
```
|
|
||||||
- **Issue**: No validation on diagram size or content - could lead to DoS via large diagrams
|
|
||||||
- **Recommendation**: Add size limits and timeout
|
|
||||||
|
|
||||||
### Major Issues
|
|
||||||
|
|
||||||
**3. CSRF trusted origins only for HTTPS** (`/home/adebaumann/development/vgui-cicd/VorgabenUI/settings.py`, line 104)
|
|
||||||
```python
|
|
||||||
CSRF_TRUSTED_ORIGINS=["https://vorgabenportal.knowyoursecurity.com"]
|
|
||||||
```
|
|
||||||
- **Issue**: Only one origin configured - ensure this covers all deployment URLs
|
|
||||||
- **Recommendation**: Make configurable via environment variable
|
|
||||||
|
|
||||||
**4. No session expiry configuration** (`/home/adebaumann/development/vgui-cicd/VorgabenUI/settings.py`)
|
|
||||||
- **Issue**: Sessions don't expire
|
|
||||||
- **Recommendation**: Set `SESSION_COOKIE_AGE` and `SESSION_SAVE_EVERY_REQUEST`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 11. CODE STYLE COMPLIANCE (AGENTS.md)
|
|
||||||
|
|
||||||
### Violations Found
|
|
||||||
|
|
||||||
**1. Import order inconsistent** (`/home/adebaumann/development/vgui-cicd/dokumente/views.py`, lines 1-16)
|
|
||||||
- Imports are not strictly ordered (stdlib, Django, local apps)
|
|
||||||
- Example: `import parsedatetime` is placed after Django imports
|
|
||||||
|
|
||||||
**2. Missing German `verbose_name` on models** (as noted in Section 3)
|
|
||||||
|
|
||||||
**3. Function naming** (`/home/adebaumann/development/vgui-cicd/dokumente/models.py`, line 101)
|
|
||||||
```python
|
|
||||||
def Vorgabennummer(self): # Should be vorgabennummer (snake_case)
|
|
||||||
```
|
|
||||||
- **Issue**: Method name uses PascalCase instead of snake_case per AGENTS.md guidelines
|
|
||||||
|
|
||||||
**4. Typo in error message** (`/home/adebaumann/development/vgui-cicd/dokumente/models.py`, line 213)
|
|
||||||
```python
|
|
||||||
"Geltungsdauer übeschneidet sich" # Should be "überschneidet sich"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 12. PERFORMANCE ISSUES
|
|
||||||
|
|
||||||
### Major Issues
|
|
||||||
|
|
||||||
**1. N+1 query potential in search** (`/home/adebaumann/development/vgui-cicd/pages/views.py`, lines 53-68)
|
|
||||||
```python
|
|
||||||
qs = VorgabeKurztext.objects.filter(inhalt__icontains=suchbegriff)...
|
|
||||||
```
|
|
||||||
- **Issue**: Uses `icontains` which cannot use database indexes effectively
|
|
||||||
- **Recommendation**: Consider PostgreSQL full-text search for better performance
|
|
||||||
|
|
||||||
**2. No select_related in referenzen views** (`/home/adebaumann/development/vgui-cicd/referenzen/views.py`, lines 7-8)
|
|
||||||
```python
|
|
||||||
def tree(request):
|
|
||||||
referenz_items = Referenz.objects.all()
|
|
||||||
```
|
|
||||||
- **Issue**: No prefetch_related for related data
|
|
||||||
- **Recommendation**: Add prefetch_related for `referenzerklaerung_set` and `unterreferenzen`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## SUMMARY
|
|
||||||
|
|
||||||
### Critical Issues (Must Fix)
|
|
||||||
| # | Issue | Location | Recommendation |
|
|
||||||
|---|-------|----------|----------------|
|
|
||||||
| 1 | SECRET_KEY fallback | `VorgabenUI/settings.py:27-47` | Never enable fallback, require env var |
|
|
||||||
| 2 | DEBUG defaults to True | `VorgabenUI/settings.py:24` | Require explicit False for production |
|
|
||||||
| 3 | No rate limiting | All views | Add django-ratelimit |
|
|
||||||
| 4 | Session never expires | `VorgabenUI/settings.py` | Set SESSION_COOKIE_AGE |
|
|
||||||
| 5 | XSS via `\|safe` filter | `standard_detail.html:163-164` | Use `escape` filter |
|
|
||||||
|
|
||||||
### Major Issues (Should Fix)
|
|
||||||
| # | Issue | Location | Recommendation |
|
|
||||||
|---|-------|----------|----------------|
|
|
||||||
| 1 | SQLite database | `VorgabenUI/settings.py:109-114` | Use PostgreSQL for production |
|
|
||||||
| 2 | ALLOWED_HOSTS wildcard | `VorgabenUI/settings.py:50` | Remove `*` from default |
|
|
||||||
| 3 | Missing date constraint | `dokumente/models.py:96-97` | Add validation for date ranges |
|
|
||||||
| 4 | Import not atomic | `import-document.py:288-349` | Wrap in transaction.atomic() |
|
|
||||||
| 5 | Missing test coverage | Multiple modules | Add tests for untested code |
|
|
||||||
|
|
||||||
### Minor Issues (Nice to Fix)
|
|
||||||
| # | Issue | Location |
|
|
||||||
|---|-------|----------|
|
|
||||||
| 1 | Code style violations | `dokumente/views.py:1-16` |
|
|
||||||
| 2 | Typo: "übeschneidet" | `dokumente/models.py:213` |
|
|
||||||
| 3 | Missing ARIA labels | `base.html` |
|
|
||||||
| 4 | Hardcoded URLs in templates | `incomplete_vorgaben.html:21` |
|
|
||||||
| 5 | Duplicate AUTH_PASSWORD_VALIDATORS | `settings-docker.py:92-105, 183-199` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## RECOMMENDED ACTIONS
|
|
||||||
|
|
||||||
### Immediate (Before Production)
|
|
||||||
1. Set up proper SECRET_KEY via environment variable
|
|
||||||
2. Configure DEBUG=False explicitly
|
|
||||||
3. Remove wildcard from ALLOWED_HOSTS default
|
|
||||||
4. Add rate limiting to all endpoints
|
|
||||||
5. Configure session expiry
|
|
||||||
6. Fix XSS vulnerability in template
|
|
||||||
7. Switch to PostgreSQL database
|
|
||||||
|
|
||||||
### Short-term (1-2 Weeks)
|
|
||||||
1. Add database constraints for date validation
|
|
||||||
2. Wrap import command in transactions
|
|
||||||
3. Add missing verbose_name to models
|
|
||||||
4. Fix code style violations
|
|
||||||
5. Add test coverage for critical paths
|
|
||||||
6. Move inline JavaScript to external files
|
|
||||||
|
|
||||||
### Long-term (1 Month)
|
|
||||||
1. Implement PostgreSQL full-text search
|
|
||||||
2. Add comprehensive test suite
|
|
||||||
3. Set up CSP headers properly
|
|
||||||
4. Implement comprehensive authentication security
|
|
||||||
5. Add performance monitoring
|
|
||||||
6. Document all security configurations
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## POSITIVE FINDINGS
|
|
||||||
|
|
||||||
1. **Good project organization** - Clear app structure following Django conventions
|
|
||||||
2. **Proper CSRF handling** - X-CSRFToken header properly implemented
|
|
||||||
3. **Input validation** - Comment length limits and dangerous pattern checks in place
|
|
||||||
4. **Good German localization** - German field names and verbose texts throughout
|
|
||||||
5. **Django admin integration** - Well-configured admin interface
|
|
||||||
6. **Management commands** - Useful import/export functionality
|
|
||||||
7. **Referenzen tree structure** - MPTT implementation for hierarchical data
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## APPENDIX: Additional Issues Detected by LSP
|
|
||||||
|
|
||||||
The following issues were detected by the Language Server Protocol (LSP) analyzer:
|
|
||||||
|
|
||||||
### dokumente/models.py
|
|
||||||
| Line | Issue |
|
|
||||||
|------|-------|
|
|
||||||
| 14, 26, 36, 274 | `__str__` method return type mismatch - returns `CharField` instead of `str` |
|
|
||||||
| 70 | Unknown attribute `vorgaben` on `Dokument` |
|
|
||||||
| 102 | Unknown attribute `nummer` on `ForeignKey` |
|
|
||||||
| 106, 114 | Unknown attribute `strftime` on `DateField` |
|
|
||||||
| 137, 144, 196 | Unknown attribute `objects` on `type[Vorgabe]` |
|
|
||||||
| 173 | Unknown attribute `thema_id` on `Vorgabe` |
|
|
||||||
| 248, 254, 260, 266 | `Meta` class overrides incompatible parent class |
|
|
||||||
| 282 | `VorgabenTable.Meta` incompatible with `Vorgabe.Meta` |
|
|
||||||
| 294 | Unknown attribute `nummer` on `ForeignKey` |
|
|
||||||
| 314 | Unknown attributes `username` and `Vorgabennummer` on `ForeignKey` |
|
|
||||||
|
|
||||||
### dokumente/views.py
|
|
||||||
| Line | Issue |
|
|
||||||
|------|-------|
|
|
||||||
| 22, 133, 265 | Unknown attribute `objects` on `type[Dokument]` |
|
|
||||||
| 94 | Unknown attribute `objects` on `type[Vorgabe]` |
|
|
||||||
| 285 | Argument type `str` cannot be assigned to parameter `content` of type `bytes` |
|
|
||||||
| 345, 412, 440 | Unknown attribute `objects` on `type[VorgabeComment]` |
|
|
||||||
|
|
||||||
### abschnitte/models.py
|
|
||||||
| Line | Issue |
|
|
||||||
|------|-------|
|
|
||||||
| 6 | `__str__` method return type mismatch |
|
|
||||||
| 18 | Argument type `Literal[0]` cannot be assigned to parameter `default` |
|
|
||||||
|
|
||||||
### referenzen/models.py
|
|
||||||
| Line | Issue |
|
|
||||||
|------|-------|
|
|
||||||
| 23 | `__str__` method return type mismatch |
|
|
||||||
| 26 | `Referenz.Meta` overrides incompatible parent `MPTTModel.Meta` |
|
|
||||||
| 32 | `Referenzerklaerung.Meta` overrides incompatible parent `Textabschnitt.Meta` |
|
|
||||||
|
|
||||||
### rollen/models.py
|
|
||||||
| Line | Issue |
|
|
||||||
|------|-------|
|
|
||||||
| 8 | `__str__` method return type mismatch |
|
|
||||||
| 15 | `RollenBeschreibung.Meta` overrides incompatible parent `Textabschnitt.Meta` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*End of Code Review*
|
|
||||||
@@ -12,7 +12,7 @@ class Dokumententyp(models.Model):
|
|||||||
verantwortliche_ve = models.CharField(max_length=255)
|
verantwortliche_ve = models.CharField(max_length=255)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.name)
|
return self.name
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name="Dokumententyp"
|
verbose_name="Dokumententyp"
|
||||||
@@ -28,7 +28,6 @@ class Person(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural="Personen"
|
verbose_name_plural="Personen"
|
||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
verbose_name="Person"
|
|
||||||
|
|
||||||
class Thema(models.Model):
|
class Thema(models.Model):
|
||||||
name = models.CharField(max_length=100, primary_key=True)
|
name = models.CharField(max_length=100, primary_key=True)
|
||||||
@@ -38,7 +37,7 @@ class Thema(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural="Themen"
|
verbose_name_plural="Themen"
|
||||||
verbose_name="Thema"
|
|
||||||
|
|
||||||
class Dokument(models.Model):
|
class Dokument(models.Model):
|
||||||
nummer = models.CharField(max_length=50, primary_key=True)
|
nummer = models.CharField(max_length=50, primary_key=True)
|
||||||
@@ -50,7 +49,7 @@ class Dokument(models.Model):
|
|||||||
gueltigkeit_bis = models.DateField(null=True, blank=True)
|
gueltigkeit_bis = models.DateField(null=True, blank=True)
|
||||||
signatur_cso = models.CharField(max_length=255, blank=True)
|
signatur_cso = models.CharField(max_length=255, blank=True)
|
||||||
anhaenge = models.TextField(blank=True)
|
anhaenge = models.TextField(blank=True)
|
||||||
aktiv = models.BooleanField(blank=True,default=False)
|
aktiv = models.BooleanField(blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.nummer} – {self.name}"
|
return f"{self.nummer} – {self.name}"
|
||||||
|
|||||||
19
k8s/data-loader-pod.yaml
Normal file
19
k8s/data-loader-pod.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: data-loader
|
||||||
|
namespace: vorgabenui
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: loader
|
||||||
|
image: adebaumann/vgui-preloader:0.5
|
||||||
|
command: ["sh","-c","cp -v --debug --update=none /preload/preload.sqlite3 /data/db.sqlite3; chown -R 999:999 /data; ls -la /data; exit 0"]
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: django-data-pvc
|
||||||
BIN
k8s/db/db.sqlite3
Normal file
BIN
k8s/db/db.sqlite3
Normal file
Binary file not shown.
68
k8s/deployment.yaml
Normal file
68
k8s/deployment.yaml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: django
|
||||||
|
namespace: vorgabenui
|
||||||
|
spec:
|
||||||
|
replicas: 10
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: django
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: django
|
||||||
|
spec:
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 999
|
||||||
|
fsGroupChangePolicy: "OnRootMismatch"
|
||||||
|
initContainers:
|
||||||
|
- name: loader
|
||||||
|
image: adebaumann/vgui-preloader:0.5
|
||||||
|
command: [ "sh","-c","cp -v --debug --update=none /preload/preload.sqlite3 /data/db.sqlite3; chown -R 999:999 /data; ls -la /data; exit 0" ]
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
containers:
|
||||||
|
- name: web
|
||||||
|
image: docker.io/adebaumann/vui:0.918
|
||||||
|
imagePullPolicy: Always
|
||||||
|
ports:
|
||||||
|
- containerPort: 8000
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /app/data
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 8000
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 2
|
||||||
|
failureThreshold: 6
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 8000
|
||||||
|
initialDelaySeconds: 20
|
||||||
|
periodSeconds: 20
|
||||||
|
timeoutSeconds: 2
|
||||||
|
failureThreshold: 3
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: django-data-pvc
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: django
|
||||||
|
namespace: vorgabenui
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: django
|
||||||
|
ports:
|
||||||
|
- port: 8000
|
||||||
|
targetPort: 8000
|
||||||
|
|
||||||
60
k8s/diagrammer.yaml
Normal file
60
k8s/diagrammer.yaml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: kroki
|
||||||
|
namespace: vorgabenui
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: kroki
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: kroki
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: kroki
|
||||||
|
image: docker.io/yuzutech/kroki:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8000
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 8000
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 2
|
||||||
|
failureThreshold: 6
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 8000
|
||||||
|
initialDelaySeconds: 20
|
||||||
|
periodSeconds: 20
|
||||||
|
timeoutSeconds: 2
|
||||||
|
failureThreshold: 3
|
||||||
|
- name: mermaid
|
||||||
|
image: docker.io/yuzutech/kroki-mermaid:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8002
|
||||||
|
- name: bpmn
|
||||||
|
image: docker.io/yuzutech/kroki-bpmn:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8003
|
||||||
|
- name: excalidraw
|
||||||
|
image: docker.io/yuzutech/kroki-excalidraw:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8004
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: svckroki
|
||||||
|
namespace: vorgabenui
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: kroki
|
||||||
|
ports:
|
||||||
|
- port: 8000
|
||||||
|
targetPort: 8000
|
||||||
52
k8s/django-deployment-example.yaml
Normal file
52
k8s/django-deployment-example.yaml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: vgui-cicd-django
|
||||||
|
namespace: vorgabenui
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: vgui-cicd-django
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: vgui-cicd-django
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: django
|
||||||
|
image: your-django-image:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8000
|
||||||
|
env:
|
||||||
|
# Django SECRET_KEY from Kubernetes secret
|
||||||
|
- name: VORGABENUI_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: vorgabenui-secrets
|
||||||
|
key: vorgabenui_secret
|
||||||
|
# Other environment variables can be added here
|
||||||
|
- name: DEBUG
|
||||||
|
value: "False"
|
||||||
|
# Add database configuration, etc.
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "250m"
|
||||||
|
limits:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "500m"
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: vgui-cicd-django-service
|
||||||
|
namespace: vorgabenui
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: vgui-cicd-django
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 80
|
||||||
|
targetPort: 8000
|
||||||
|
type: ClusterIP
|
||||||
9
k8s/django-secret.yaml
Normal file
9
k8s/django-secret.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: vorgabenui-secrets
|
||||||
|
namespace: vorgabenui
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
# Base64 encoded SECRET_KEY - will be populated by deployment script
|
||||||
|
vorgabenui_secret: ""
|
||||||
19
k8s/ingress.yaml
Normal file
19
k8s/ingress.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: django
|
||||||
|
namespace: vorgabenui
|
||||||
|
annotations:
|
||||||
|
traefik.ingress.kubernetes.io/router.middlewares: "vorgabenui-vorgabenui-rewrite@kubernetescrd"
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: vorgabenui.adebaumann.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: django
|
||||||
|
port:
|
||||||
|
number: 8000
|
||||||
18
k8s/ingressdebug.yaml
Normal file
18
k8s/ingressdebug.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: django
|
||||||
|
namespace: vorgabenui
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
|
rules:
|
||||||
|
- host: vorgabenui.local
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: django
|
||||||
|
port:
|
||||||
|
number: 8000
|
||||||
15
k8s/nfs-pv.yaml
Normal file
15
k8s/nfs-pv.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: django-data-pv
|
||||||
|
namespace: vorgabenui
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: 2Gi
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
persistentVolumeReclaimPolicy: Retain
|
||||||
|
storageClassName: nfs
|
||||||
|
nfs:
|
||||||
|
server: 192.168.17.199
|
||||||
|
path: /mnt/user/vorgabenui
|
||||||
8
k8s/nfs-storageclass.yaml
Normal file
8
k8s/nfs-storageclass.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: nfs
|
||||||
|
provisioner: kubernetes.io/no-provisioner
|
||||||
|
allowVolumeExpansion: true
|
||||||
|
reclaimPolicy: Retain
|
||||||
|
volumeBindingMode: Immediate
|
||||||
13
k8s/pvc.yaml
Normal file
13
k8s/pvc.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: django-data-pvc
|
||||||
|
namespace: vorgabenui
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
storageClassName: nfs
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 2Gi
|
||||||
|
|
||||||
9
k8s/traefik-middleware.yaml
Normal file
9
k8s/traefik-middleware.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: vorgabenui-rewrite
|
||||||
|
namespace: vorgabenui
|
||||||
|
spec:
|
||||||
|
stripPrefix:
|
||||||
|
prefixes:
|
||||||
|
- "/"
|
||||||
@@ -219,7 +219,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 text-right">
|
<div class="col-sm-6 text-right">
|
||||||
<p class="text-muted">Version {{ version|default:"0.983" }}</p>
|
<p class="text-muted">Version {{ version|default:"0.980" }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 6.0.1 on 2026-01-20 08:57
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('referenzen', '0003_alter_referenzerklaerung_options'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='referenz',
|
|
||||||
options={'verbose_name': 'Referenz', 'verbose_name_plural': 'Referenzen'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -25,7 +25,6 @@ class Referenz(MPTTModel):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural="Referenzen"
|
verbose_name_plural="Referenzen"
|
||||||
verbose_name="Referenz"
|
|
||||||
|
|
||||||
class Referenzerklaerung (Textabschnitt):
|
class Referenzerklaerung (Textabschnitt):
|
||||||
erklaerung = models.ForeignKey(Referenz,on_delete=models.CASCADE)
|
erklaerung = models.ForeignKey(Referenz,on_delete=models.CASCADE)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ def tree(request):
|
|||||||
|
|
||||||
|
|
||||||
def detail(request, refid):
|
def detail(request, refid):
|
||||||
referenz_item = Referenz.objects.get_object_or_404(id=refid)
|
referenz_item = Referenz.objects.get(id=refid)
|
||||||
referenz_item.erklaerung = render_textabschnitte(referenz_item.referenzerklaerung_set.order_by("order"))
|
referenz_item.erklaerung = render_textabschnitte(referenz_item.referenzerklaerung_set.order_by("order"))
|
||||||
referenz_item.children = list(referenz_item.get_descendants(include_self=True))
|
referenz_item.children = list(referenz_item.get_descendants(include_self=True))
|
||||||
for child in referenz_item.children:
|
for child in referenz_item.children:
|
||||||
|
|||||||
@@ -1,44 +1,37 @@
|
|||||||
appdirs==1.4.4
|
appdirs==1.4.4
|
||||||
asgiref==3.11.0
|
asgiref==3.8.1
|
||||||
bleach==6.3.0
|
blessed==1.21.0
|
||||||
blessed==1.27.0
|
certifi==2025.8.3
|
||||||
certifi==2026.1.4
|
charset-normalizer==3.4.3
|
||||||
charset-normalizer==3.4.4
|
|
||||||
coverage==7.13.1
|
|
||||||
curtsies==0.4.3
|
curtsies==0.4.3
|
||||||
cwcwidth==0.1.12
|
cwcwidth==0.1.10
|
||||||
Django==6.0.1
|
Django==5.2.9
|
||||||
django-admin-sortable2==2.3
|
django-admin-sortable2==2.2.8
|
||||||
django-js-asset==3.1.2
|
django-js-asset==3.1.2
|
||||||
django-mptt==0.18.0
|
django-mptt==0.17.0
|
||||||
django-mptt-admin==2.9.0
|
django-mptt-admin==2.8.0
|
||||||
django-nested-admin==4.1.6
|
django-nested-admin==4.1.1
|
||||||
django-nested-inline==0.4.6
|
django-nested-inline==0.4.6
|
||||||
django-revproxy==0.13.0
|
django-revproxy==0.13.0
|
||||||
greenlet==3.3.0
|
greenlet==3.2.4
|
||||||
gunicorn==23.0.0
|
gunicorn==23.0.0
|
||||||
idna==3.11
|
idna==3.10
|
||||||
jedi==0.19.2
|
jedi==0.19.2
|
||||||
jproperties==2.1.2
|
Markdown==3.8.2
|
||||||
Markdown==3.10
|
|
||||||
packaging==25.0
|
packaging==25.0
|
||||||
parsedatetime==2.6
|
parsedatetime==2.6
|
||||||
parso==0.8.5
|
parso==0.8.4
|
||||||
pep8==1.7.1
|
pep8==1.7.1
|
||||||
prompt_toolkit==3.0.52
|
prompt_toolkit==3.0.51
|
||||||
pyfakefs==5.9.3
|
|
||||||
Pygments==2.19.2
|
Pygments==2.19.2
|
||||||
pysonar==1.2.1.3951
|
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
python-monkey-business==1.1.0
|
python-monkey-business==1.1.0
|
||||||
pyxdg==0.28
|
pyxdg==0.28
|
||||||
PyYAML==6.0.3
|
|
||||||
requests==2.32.5
|
requests==2.32.5
|
||||||
responses==0.25.8
|
|
||||||
six==1.17.0
|
six==1.17.0
|
||||||
sqlparse==0.5.5
|
sqlparse==0.5.3
|
||||||
tomli==2.2.1
|
|
||||||
urllib3==2.6.3
|
urllib3==2.6.3
|
||||||
wcwidth==0.2.14
|
wcwidth==0.2.13
|
||||||
webencodings==0.5.1
|
bleach==6.1.0
|
||||||
whitenoise==6.11.0
|
coverage==7.6.1
|
||||||
|
whitenoise==6.8.2
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 6.0.1 on 2026-01-20 08:57
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('rollen', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='rolle',
|
|
||||||
options={'verbose_name': 'Rolle (für Relevanz)', 'verbose_name_plural': 'Rolleni (für Relevanz)'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -9,10 +9,9 @@ class Rolle(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural="Rollen"
|
verbose_name_plural="Rollen"
|
||||||
verbose_name="Rolle"
|
|
||||||
|
|
||||||
class RollenBeschreibung(Textabschnitt):
|
class RollenBeschreibung(Textabschnitt):
|
||||||
abschnitt=models.ForeignKey(Rolle,on_delete=models.CASCADE)
|
abschnitt=models.ForeignKey(Rolle,on_delete=models.CASCADE)
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural="Rollenbeschreibung"
|
verbose_name_plural="Rollenbeschreibung"
|
||||||
verbose_name="Rollenbeschreibungs-Abschnitt"
|
verbose_name="Rollenbeschreibungs-Abschnitt"
|
||||||
201
scripts/deploy-django-secret.sh
Executable file
201
scripts/deploy-django-secret.sh
Executable file
@@ -0,0 +1,201 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# deploy-django-secret.sh
|
||||||
|
# Script to generate a secure Django SECRET_KEY and deploy it to Kubernetes
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
NAMESPACE="${NAMESPACE:-vorgabenui}"
|
||||||
|
SECRET_NAME="vorgabenui-secrets"
|
||||||
|
SECRET_KEY_NAME="vorgabenui_secret"
|
||||||
|
K8S_DIR="$(dirname "$0")/../k8s"
|
||||||
|
SECRET_YAML="$K8S_DIR/django-secret.yaml"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Logging functions
|
||||||
|
log_info() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warn() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to generate a secure Django SECRET_KEY
|
||||||
|
generate_secret_key() {
|
||||||
|
# Generate a 50-character secret key using Python (same as Django's default)
|
||||||
|
python3 -c "
|
||||||
|
import secrets
|
||||||
|
import string
|
||||||
|
|
||||||
|
# Django-style secret key generation
|
||||||
|
chars = string.ascii_letters + string.digits + '!@#$%^&*(-_=+)'
|
||||||
|
print(''.join(secrets.choice(chars) for _ in range(50)))
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if kubectl is available
|
||||||
|
check_kubectl() {
|
||||||
|
if ! command -v kubectl &> /dev/null; then
|
||||||
|
log_error "kubectl is not installed or not in PATH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if Python3 is available
|
||||||
|
check_python() {
|
||||||
|
if ! command -v python3 &> /dev/null; then
|
||||||
|
log_error "python3 is not installed or not in PATH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to create the secret
|
||||||
|
create_secret() {
|
||||||
|
local secret_key="$1"
|
||||||
|
local encoded_key
|
||||||
|
|
||||||
|
# Base64 encode the secret key
|
||||||
|
encoded_key=$(echo -n "$secret_key" | base64 -w 0)
|
||||||
|
|
||||||
|
log_info "Creating Kubernetes secret '$SECRET_NAME' in namespace '$NAMESPACE'..."
|
||||||
|
|
||||||
|
# Create the secret directly with kubectl
|
||||||
|
kubectl create secret generic "$SECRET_NAME" \
|
||||||
|
--from-literal="$SECRET_KEY_NAME=$secret_key" \
|
||||||
|
--namespace="$NAMESPACE" \
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log_info "Successfully created/updated secret '$SECRET_NAME'"
|
||||||
|
else
|
||||||
|
log_error "Failed to create/update secret '$SECRET_NAME'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to verify the secret
|
||||||
|
verify_secret() {
|
||||||
|
log_info "Verifying secret deployment..."
|
||||||
|
|
||||||
|
if kubectl get secret "$SECRET_NAME" --namespace="$NAMESPACE" &> /dev/null; then
|
||||||
|
log_info "Secret '$SECRET_NAME' exists in namespace '$NAMESPACE'"
|
||||||
|
|
||||||
|
# Show secret (without revealing the actual key)
|
||||||
|
kubectl describe secret "$SECRET_NAME" --namespace="$NAMESPACE"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "Secret '$SECRET_NAME' not found in namespace '$NAMESPACE'"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to show usage
|
||||||
|
show_usage() {
|
||||||
|
echo "Usage: $0 [OPTIONS]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -n, --namespace NAMESPACE Kubernetes namespace (default: vorgabenui)"
|
||||||
|
echo " -s, --secret-name NAME Secret name (default: django-secrets)"
|
||||||
|
echo " -k, --key-name NAME Secret key name (default: django-secret-key)"
|
||||||
|
echo " -h, --help Show this help message"
|
||||||
|
echo ""
|
||||||
|
echo "Environment variables:"
|
||||||
|
echo " NAMESPACE Override default namespace"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 # Deploy to vorgabenui namespace"
|
||||||
|
echo " $0 -n production # Deploy to production namespace"
|
||||||
|
echo " NAMESPACE=staging $0 # Deploy to staging namespace"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse command line arguments
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-n|--namespace)
|
||||||
|
NAMESPACE="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-s|--secret-name)
|
||||||
|
SECRET_NAME="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-k|--key-name)
|
||||||
|
SECRET_KEY_NAME="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
show_usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_error "Unknown option: $1"
|
||||||
|
show_usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
main() {
|
||||||
|
log_info "Django SECRET_KEY Deployment Script"
|
||||||
|
log_info "==================================="
|
||||||
|
log_info "Namespace: $NAMESPACE"
|
||||||
|
log_info "Secret Name: $SECRET_NAME"
|
||||||
|
log_info "Secret Key Name: $SECRET_KEY_NAME"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Perform checks
|
||||||
|
check_kubectl
|
||||||
|
check_python
|
||||||
|
|
||||||
|
# Generate new secret key
|
||||||
|
log_info "Generating new Django SECRET_KEY..."
|
||||||
|
SECRET_KEY=$(generate_secret_key)
|
||||||
|
|
||||||
|
if [ -z "$SECRET_KEY" ]; then
|
||||||
|
log_error "Failed to generate secret key"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Generated secret key (first 10 chars): ${SECRET_KEY:0:10}..."
|
||||||
|
|
||||||
|
# Create namespace if it doesn't exist
|
||||||
|
if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then
|
||||||
|
log_warn "Namespace '$NAMESPACE' does not exist, creating..."
|
||||||
|
kubectl create namespace "$NAMESPACE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create the secret
|
||||||
|
create_secret "$SECRET_KEY"
|
||||||
|
|
||||||
|
# Verify deployment
|
||||||
|
verify_secret
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
log_info "Deployment completed successfully!"
|
||||||
|
log_info "To use this secret in your Django deployment, add the following to your pod spec:"
|
||||||
|
echo ""
|
||||||
|
echo " env:"
|
||||||
|
echo " - name: VORGABENUI_SECRET"
|
||||||
|
echo " valueFrom:"
|
||||||
|
echo " secretKeyRef:"
|
||||||
|
echo " name: $SECRET_NAME"
|
||||||
|
echo " key: $SECRET_KEY_NAME"
|
||||||
|
echo ""
|
||||||
|
log_warn "The old secret key in settings.py has been replaced with environment variable lookup."
|
||||||
|
log_warn "Make sure your Django deployment uses the environment variable before deploying."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main function
|
||||||
|
main
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
NAMESPACE="vorgabenui"
|
NAMESPACE="vorgabenui"
|
||||||
SECRET_NAME="django-secret"
|
SECRET_NAME="django-secret"
|
||||||
SECRET_FILE="templates/secret.yaml"
|
SECRET_FILE="argocd/secret.yaml"
|
||||||
|
|
||||||
# Check if secret file exists
|
# Check if secret file exists
|
||||||
if [ ! -f "$SECRET_FILE" ]; then
|
if [ ! -f "$SECRET_FILE" ]; then
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 6.0.1 on 2026-01-20 08:57
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('stichworte', '0003_alter_stichworterklaerung_options'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='stichwort',
|
|
||||||
options={'verbose_name': 'Stichwort', 'verbose_name_plural': 'Stichworte'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -9,7 +9,6 @@ class Stichwort(models.Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural="Stichworte"
|
verbose_name_plural="Stichworte"
|
||||||
verbose_name = "Stichwort"
|
|
||||||
|
|
||||||
class Stichworterklaerung (Textabschnitt):
|
class Stichworterklaerung (Textabschnitt):
|
||||||
erklaerung = models.ForeignKey(Stichwort,on_delete=models.CASCADE)
|
erklaerung = models.ForeignKey(Stichwort,on_delete=models.CASCADE)
|
||||||
|
|||||||
Reference in New Issue
Block a user