From eb2ec18ad9d3deb52715da19470725d656e9417e Mon Sep 17 00:00:00 2001 From: phg Date: Fri, 5 Sep 2025 10:39:25 +0200 Subject: [PATCH] initial commit --- .gitignore | 83 +++++++++++ README.md | 0 data/config/bitpoll/settings.py | 140 ++++++++++++++++++ .../nginx-templates/default.conf.template | 19 +++ docker-compose.yml | 100 +++++++++++++ template.env | 15 ++ 6 files changed, 357 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 data/config/bitpoll/settings.py create mode 100644 data/config/nginx-templates/default.conf.template create mode 100644 docker-compose.yml create mode 100644 template.env diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2e270ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,83 @@ +.env + + + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# Metadata left by Dolphin file manager, which comes with KDE Plasma +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# Log files created by default by the nohup command +nohup.out + +# General +.DS_Store +__MACOSX/ +.AppleDouble +.LSOverride +Icon[ +] + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets +!*.code-workspace + +# Built Visual Studio Code Extensions +*.vsix diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/data/config/bitpoll/settings.py b/data/config/bitpoll/settings.py new file mode 100644 index 0000000..d799c3c --- /dev/null +++ b/data/config/bitpoll/settings.py @@ -0,0 +1,140 @@ +# customize to your needs +import re +import os +# You must insert your own random value here +# SECURITY WARNING: keep the secret key used in production secret! +# see +SECRET_KEY = os.environ.get('BITPOLL_SECRET_KEY', '...') + +# generate via: ./manage.py generate_encryption_key +FIELD_ENCRYPTION_KEY = os.environ.get('BITPOLL_FIELD_ENCRYPTION_KEY', 'this+is+an+example+key+please+generate+one+=') + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = False + +# The domain name of the site +ALLOWED_HOSTS = ['poll.s1q.dev'] + +## If Bitpoll is served via HTTPS enable the next two options +SESSION_COOKIE_SECURE = True +CSRF_COOKIE_SECURE = True + +# The root dir bitpoll appears to be in from the web, as configured in the webserver +URL_PREFIX = '' + +#Add additionall installed apps here +## Example for installed raven (Sentry instrumentation) +#INSTALLED_APPS_LOCAL = [ +# 'raven.contrib.django.raven_compat', +# ] +INSTALLED_APPS_LOCAL = [] + +# To use OpenId: +#INSTALLED_APPS_LOCAL.append('simple_openid_connect.integrations.django') +#OPENID_ENABLED = True +#OPENID_ISSUER = "https://identity.mafiasi.de/realms/mafiasi" +#OPENID_API_BASE = "https://identity.mafiasi.de/admin/realms/mafiasi" +#OPENID_CLIENT_ID = "..." +#OPENID_CLIENT_SECRET = "..." +#OPENID_BASE_URI = "..." +#OPENID_SCOPE = "openid profile email" +#OPENID_USER_MAPPER = 'bitpoll.base.openid.BitpollUserMapper' +#OPENID_ADMIN_GROUPS = re.compile('admins|superusers') +#LOGIN_URL = "simple_openid_connect_django:login" +#LOGOUT_REDIRECT_URL = "index" + +MIDDLEWARE_LOCAL = [ +# "simple_openid_connect.integrations.django.middleware.TokenVerificationMiddleware", +] + +# Compress the JS and CSS files, for more Options see https://django-pipeline.readthedocs.io/en/latest/compressors.html +# the Compressor have to be installed in the system +PIPELINE_LOCAL = {} +#PIPELINE_LOCAL['JS_COMPRESSOR'] = 'pipeline.compressors.uglifyjs.UglifyJSCompressor' +#PIPELINE_LOCAL['CSS_COMPRESSOR'] = 'pipeline.compressors.cssmin.CSSMinCompressor' +#PIPELINE_ENABLED = True + +LANGUAGE_CODE = 'en-us' +TIME_ZONE = 'UTC' + +## https://docs.djangoproject.com/en/1.9/ref/settings/#databases +#DATABASES = { +# 'default': {+ +# 'ENGINE': 'django.db.backends.sqlite3', +# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), +# } +#} +DATABASES = { + "default": {+ + "ENGINE": "django.db.backends.postgresql", + "NAME": "bitpoll", + "USER": "bitpoll", + "PASSWORD": os.environ.get('BITPOLL_DB_PASSWORD', 'password'), + "HOST": "db", + "PORT": 5432, + "CONN_MAX_AGE": 60, + } +} + + +## Customize your instance +SITE_NAME = 's1q poll' +BASE_URL = 'https://poll.s1q.dev' + +## Url to the Base Homepage and Text on the Link, leave empty to not use this option +#HOME_URL = "https://example.com" +#HOME_URL_NAME = "Dashboard" + +## Test mail functionality by printing mails to console: +## EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' + +## if the imprint URL is not empty use it as an link to the imprint, else use IMPRINT_TEXT +#IMPRINT_URL = "" +#IMPRINT_TEXT = """ +#

ImpressuXm

+#

Text goes here

+#""" + +#LOCALE_PATHS = (os.path.join(ROOT_DIR, 'locale'), ) +LANGUAGES = ( + ('de', 'Deutsch'), + ('en', 'English'), + #('fr', 'Français'), + #('it', 'Italiano'), +) + +REGISTER_ENABLED = True +GROUP_MANAGEMENT = REGISTER_ENABLED + +## Use ldap login +#import ldap +#from django_auth_ldap.config import LDAPSearch +# +#AUTHENTICATION_BACKENDS = ( +# 'django_auth_ldap.backend.LDAPBackend', +# 'django.contrib.auth.backends.ModelBackend', +# ) +# +#AUTH_LDAP_SERVER_URI = "ldap_host" +#AUTH_LDAP_BIND_DN = "ldap_bind_dn" +#AUTH_LDAP_BIND_PASSWORD = "ldap_bind_pw" +#AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=People,dc=mafiasi,dc=de", +# ldap.SCOPE_SUBTREE, "(uid=%(user)s)") +#AUTH_LDAP_ALWAYS_UPDATE_USER = True +# +#from django_auth_ldap.config import LDAPSearch, PosixGroupType +# +#AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=groups,dc=mafiasi,dc=de", +# ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)" +# ) +#AUTH_LDAP_GROUP_TYPE = PosixGroupType() +##AUTH_LDAP_FIND_GROUP_PERMS = True +#AUTH_LDAP_MIRROR_GROUPS = True +# +#AUTH_LDAP_USER_ATTR_MAP = {"first_name": "givenName", "last_name": "sn", "email": "mail"} +# +#AUTH_LDAP_USER_FLAGS_BY_GROUP = { +# "is_staff": ["cn=Editoren,ou=groups,dc=mafiasi,dc=de", +# "cn=Server-AG,ou=groups,dc=mafiasi,dc=de"], +# "is_superuser": "cn=Server-AG,ou=groups,dc=mafiasi,dc=de" +#} diff --git a/data/config/nginx-templates/default.conf.template b/data/config/nginx-templates/default.conf.template new file mode 100644 index 0000000..8957f13 --- /dev/null +++ b/data/config/nginx-templates/default.conf.template @@ -0,0 +1,19 @@ +server { + listen 8080; + server_name default_server; + + root /var/www; + + location / { + include uwsgi_params; + uwsgi_pass bitpoll:3008; + } + + location /static { + expires 14d; + alias /opt/static; + } + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4c84814 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,100 @@ +--- + +services: + nginx: + image: ghcr.io/nginx/nginx-unprivileged:${NGINX_TAG:?NGINX_TAG is not configured} + restart: unless-stopped + depends_on: + - bitpoll + read_only: true + volumes: + # - /etc/localtime:/etc/localtime:ro + # - /etc/timezone:/etc/timezone:ro + - ./data/config/nginx-templates:/etc/nginx/templates:ro + - static:/var/www + tmpfs: + - /etc/nginx/conf.d:uid=101,gid=101 + - /tmp + - /var/cache/nginx + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + networks: + - appnet + - dokploy-network + labels: + - "traefik.enable=true" + - "traefik.docker.network=dokploy-network" + + - "traefik.http.services.bitpoll-nginx.loadbalancer.server.port=8080" # set port the container listenes to + - "traefik.http.services.bitpoll-nginx.loadbalancer.server.scheme=http" + + - "traefik.http.routers.bitpoll-nginx-web.rule=Host(`${PUBLIC_DOMAIN}`)" + - "traefik.http.routers.bitpoll-nginx-web.service=bitpoll-nginx@docker" + - "traefik.http.routers.bitpoll-nginx-web.entrypoints=web" + - "traefik.http.routers.bitpoll-nginx-web.middlewares=redirect-to-https@file" + + - "traefik.http.routers.bitpoll-nginx-websecure.rule=Host(`${PUBLIC_DOMAIN}`)" # change hostname! + - "traefik.http.routers.bitpoll-nginx-websecure.service=bitpoll-nginx@docker" + - "traefik.http.routers.bitpoll-nginx-websecure.entrypoints=websecure" + - "traefik.http.routers.bitpoll-nginx-websecure.tls=true" + - "traefik.http.routers.bitpoll-nginx-websecure.tls.options=modern@file" + - "traefik.http.routers.bitpoll-nginx-websecure.tls.certresolver=hetzner" + - "traefik.http.routers.bitpoll-nginx-websecure.tls.domains[0].main=${TLS_DOMAIN}" + - "traefik.http.routers.bitpoll-nginx-websecure.middlewares=secHeaders@file, hsts-header@file" + + bitpoll: + image: ghcr.io/fsinfuhh/bitpoll:${BITPOLL_TAG:?BITPOLL_TAG is not configured} + build: + dockerfile: Dockerfile + context: ./src + restart: unless-stopped + depends_on: + postgresql: + condition: service_healthy + environment: + BITPOLL_SECRET_KEY: ${BITPOLL_SECRET_KEY?:BITPOLL_SECRET_KEY is required!} + BITPOLL_FIELD_ENCRYPTION_KEY: ${BITPOLL_FIELD_ENCRYPTION_KEY?:BITPOLL_FIELD_ENCRYPTION_KEY is required!} + BITPOLL_DB_PASSWORD: ${BITPOLL_DB_PASSWORD?:BITPOLL_DB_PASSWORD is required!} + networks: + - appnet + volumes: + # - /etc/localtime:/etc/localtime:ro + # - /etc/timezone:/etc/timezone:ro + - static:/opt/static + - ./data/config/bitpoll:/opt/config + - log:/opt/log + + db: + image: postgres:${POSTGRES_TAG:?POSTGRES_TAG is not configured} + restart: unless-stopped + environment: + POSTGRES_USER: bitpoll + POSTGRES_DB: bitpoll + POSTGRES_PASSWORD: ${BITPOLL_DB_PASSWORD?:BITPOLL_DB_PASSWORD is required!} + healthcheck: + interval: 30s + retries: 5 + start_period: 20s + test: + - CMD-SHELL + - pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER} + timeout: 5s + networks: + - appnet + volumes: + - db:/var/lib/postgresql/data:rw + +volumes: + static: + driver: local + log: + driver: local + db: + driver: local + +networks: + appnet: + dokploy-network: + # external: true diff --git a/template.env b/template.env new file mode 100644 index 0000000..d3ac2c4 --- /dev/null +++ b/template.env @@ -0,0 +1,15 @@ +# SETTINGS from env.template +# Misc configuration +COMPOSE_PROJECT_NAME=poll-s1q-dev +PUBLIC_DOMAIN=poll.s1q.dev +TLS_DOMAIN=*.s1q.dev + +# Container Tags +NGINX_TAG=1.29.1 +BITPOLL_TAG=latest +POSTGRES_TAG=17.6 + +# Secrets +# BITPOLL_SECRET_KEY="" # generate via: openssl rand -base64 128 | tr -d '\n' +# BITPOLL_FIELD_ENCRYPTION_KEY="" # generate via: ./manage.py generate_encryption_key / docker run --rm --volume ./data/config/bitpoll:/opt/config --entrypoint ./manage.py ghcr.io/fsinfuhh/bitpoll:latest generate_encryption_key +# BITPOLL_DB_PASSWORD="password" # generate via: openssl rand -base64 128 | tr -d '\n'