Compare commits

...

29 Commits

Author SHA1 Message Date
e0675c8684 Merge pull request 'Update .env.template' (#18) from woutervermeer-patch-1 into main
Some checks failed
Gitea build & Test. / tests (push) Failing after 5s
Deploy to production / deploy (push) Failing after 31s
Gitea build & Test. / tests (pull_request) Failing after 6s
Gitea Test. / tests (pull_request) Successful in 6s
Reviewed-on: #18
2026-03-26 09:52:42 +00:00
4b18769137 Update .env.template
Some checks failed
Gitea build & Test. / tests (pull_request) Failing after 7s
Gitea Test. / tests (pull_request) Successful in 6s
2026-03-26 09:52:33 +00:00
90b7bb0dee Merge pull request 'pre-prod' (#17) from pre-prod into main
Some checks failed
Gitea build & Test. / tests (push) Failing after 6s
Deploy to production / deploy (push) Failing after 1m19s
Reviewed-on: #17
2026-03-26 08:17:36 +00:00
8cfe5c6f9b Add .gitea/workflows/test.yaml
Some checks failed
Gitea Test. / tests (push) Successful in 6s
Gitea build & Test. / tests (pull_request) Failing after 6s
Gitea Test. / tests (pull_request) Successful in 6s
2026-03-26 08:15:11 +00:00
9397c2d4f2 Update .gitea/workflows/build_test.yaml
Some checks failed
Gitea build & Test. / tests (pull_request) Failing after 6s
2026-03-26 08:14:25 +00:00
313702f9fe Update Makefile
Some checks failed
Gitea build & Test. / tests (push) Failing after 13s
Gitea build & Test. / tests (pull_request) Failing after 7s
2026-03-26 08:09:28 +00:00
WGAVermeer
49199b7184 slightly updated README.md
Some checks failed
Gitea build & Test. / tests (push) Failing after 7s
2026-03-26 08:27:10 +01:00
WGAVermeer
c17e3e85f2 added auto-reload when developing.
Some checks failed
Gitea build & Test. / tests (push) Failing after 21s
2026-03-25 17:11:14 +01:00
WGAVermeer
be54afa093 fix makefile pt2
Some checks failed
Gitea build & Test. / tests (push) Failing after 27s
2026-03-25 15:47:29 +01:00
WGAVermeer
fd5414bd5a fix type in makefile
Some checks failed
Gitea build & Test. / tests (push) Failing after 2s
2026-03-25 15:46:46 +01:00
WGAVermeer
6b635955eb Merge branch 'pre-prod' of https://gitea.woutervermeer.com/woutervermeer/Quatsh-Website into pre-prod
Some checks failed
Gitea build & Test. / tests (push) Failing after 3s
2026-03-25 15:44:27 +01:00
WGAVermeer
0610053a3e added nginx as a proxy. Still need to enable ssl. 2026-03-25 15:44:17 +01:00
79560c5835 Merge pull request 'woutervermeer-patch-1' (#16) from woutervermeer-patch-1 into pre-prod
Some checks failed
Gitea build & Test. / tests (push) Failing after 6s
Reviewed-on: woutervermeer/Quatsh-Website#16
2026-03-11 08:49:35 +00:00
c1aa69dd8b Update .gitea/workflows/build_test.yaml
Some checks failed
Gitea build & Test. / tests (pull_request) Failing after 6s
2026-03-11 08:49:20 +00:00
d17b2f0a2a Merge pull request 'Change workflow stuff, update Readme, update .env handling' (#15) from pre-prod into main
Some checks failed
Gitea build & Test. / tests (push) Successful in 5s
Deploy to production / deploy (push) Failing after 26s
Reviewed-on: woutervermeer/Quatsh-Website#15
2026-03-11 08:39:52 +00:00
41a61fb318 Update .gitea/workflows/build_test.yaml
All checks were successful
Gitea build & Test. / tests (push) Successful in 6s
Gitea build & Test. / tests (pull_request) Successful in 5s
2026-03-11 08:38:41 +00:00
cba945375c Update .gitea/workflows/deploy.yml
All checks were successful
Gitea build & Test. / tests (push) Successful in 6s
2026-03-11 08:38:29 +00:00
WGAVermeer
1c545638b9 run deploy check aswell. 2026-03-11 09:26:45 +01:00
WGAVermeer
5fa195a0dc Merge branch 'pre-prod' of https://gitea.woutervermeer.com/woutervermeer/Quatsh-Website into pre-prod
All checks were successful
Gitea build & Test. / tests (push) Successful in 18s
2026-03-11 09:24:52 +01:00
WGAVermeer
f8dc8fc684 remove hardcoded .env 2026-03-11 09:24:45 +01:00
df587931b5 Merge pull request 'woutervermeer-patch-1' (#14) from woutervermeer-patch-1 into pre-prod
Some checks failed
Gitea build & Test. / tests (push) Failing after 3s
Reviewed-on: woutervermeer/Quatsh-Website#14
2026-03-11 08:23:40 +00:00
WGAVermeer
01ea42b64a default to non-debug mode.
Some checks failed
Gitea build & Test. / tests (push) Failing after 3s
2026-03-11 09:22:58 +01:00
WGAVermeer
fe6c0df123 Updated settings to import secret key.
Some checks failed
Gitea build & Test. / tests (push) Failing after 3s
2026-03-11 09:21:36 +01:00
357c81ff08 Update README.md
All checks were successful
Gitea build & Test. / tests (pull_request) Successful in 6s
2026-03-11 07:30:02 +00:00
WGAVermeer
26c6f46531 added poll app for testing purposes. Will be removed later.
Some checks failed
Gitea build & Test. / tests (push) Failing after 3s
2026-03-11 08:13:35 +01:00
WGAVermeer
13a317d3df Make Dev enters into the webserver shell.
Fixed database env variables to properly connect.
2026-03-11 08:12:07 +01:00
026bce1f16 Merge pull request 'Update README.md' (#13) from woutervermeer-patch-1 into main
All checks were successful
Deploy to production / deploy (push) Successful in 18s
Reviewed-on: woutervermeer/Quatsh-Website#13
2026-03-09 12:41:08 +00:00
b1f44f2ce0 Update README.md
All checks were successful
Gitea build & Test. / tests (pull_request) Successful in 6s
2026-03-09 12:40:37 +00:00
761227e05e Merge pull request 'pre-prod' (#12) from pre-prod into main
All checks were successful
Deploy to production / deploy (push) Successful in 19s
Reviewed-on: woutervermeer/Quatsh-Website#12
2026-03-08 14:34:16 +00:00
28 changed files with 364 additions and 19 deletions

View File

@@ -1,11 +1,12 @@
services:
web:
command: python -Wa manage.py test --noinput --parallel
command: python manage.py check --deploy
restart: "no"
env_file:
- path: .env.template
required: true
db:
env_file:
- path: .env.template

View File

@@ -1,6 +1,6 @@
services:
web:
command: gunicorn -b 0.0.0.0:8000 website.wsgi:application
command: gunicorn --capture-output --enable-stdio-inheritance -b 0.0.0.0:8000 website.wsgi:application
volumes:
- ./src:/src
env_file:

View File

@@ -0,0 +1,14 @@
services:
web:
command: python manage.py check --deploy; python -Wa manage.py test --noinput --parallel
restart: "no"
env_file:
- path: .env.template
required: true
db:
env_file:
- path: .env.template
required: true

View File

@@ -1,8 +1,11 @@
#NGINX
NGINX_HOSTNAME=localhost
# Django
DJANGO_SETTINGS_MODULE=website.settings
DJANGO_SECRET_KEY=CWHZCAZBNV57tDkwGHJwTUu3PCSnGG45
DEBUG=TRUE
ALLOWED_HOSTS=localhost
#ALLOWED_HOSTS=localhost
# Database (PostgreSQL)
POSTGRES_USER=test_user
@@ -14,4 +17,4 @@ POSTGRES_DB=test_db
# Gunicorn
GUNICORN_WORKERS=3
GUNICORN_TIMEOUT=120
GUNICORN_TIMEOUT=120

View File

@@ -3,7 +3,7 @@ run-name: ${{ gitea.actor }}
on:
push:
branches:
- pre-prod
- main
pull_request:
jobs:
@@ -15,3 +15,6 @@ jobs:
- name: Build and test
run: make test
- name: Build
run: make prod

View File

@@ -31,5 +31,8 @@ jobs:
echo "Building containers..."
make prod
echo "Running Django deploy check"
docker compose run --rm web python manage.py check --deploy
echo "Deployment complete."
EOF

View File

@@ -0,0 +1,17 @@
name: Gitea Test.
run-name: ${{ gitea.actor }}
on:
push:
branches:
- pre-prod
pull_request:
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: test
run: make test

4
.gitignore vendored
View File

@@ -175,3 +175,7 @@ cython_debug/
# PyPI configuration file
.pypirc
# gunicon webserver
gunicorn.ctl

3
.nginx/.conf Normal file
View File

@@ -0,0 +1,3 @@
events {}
http {}

View File

@@ -0,0 +1,49 @@
server {
listen ${NGINX_PORT};
server_name ${NGINX_HOSTNAME};
location /static/ {
alias /app/static/;
expires 30d;
add_header Cache-Control "public";
}
location /media/ {
alias /app/media;
expires 30d;
add_header Cache-Control "public";
}
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen ${NGINX_SSL_PORT};
server_name ${NGINX_HOSTNAME};
location /static/ {
alias /app/static/;
expires 30d;
add_header Cache-Control "public";
}
location /media/ {
alias /app/media;
expires 30d;
add_header Cache-Control "public";
}
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

View File

@@ -1,10 +1,26 @@
prod:
docker compose down
docker compose -f ./compose.yaml -f ./compose.prod.yaml up -d --build
docker compose --env-file .env -f ./compose.yaml -f ./.docker-compose-files/compose.prod.yaml up -d --build
docker exec quatsh-website-web-1 python manage.py collectstatic --noinput
docker exec quatsh-website-web-1 python manage.py check --deploy
docker exec quatsh-website-web-1 python manage.py migrate
dev:
docker compose down
docker compose -f ./compose.yaml -f ./compose.dev.yaml up --build
docker compose -f ./compose.yaml -f ./.docker-compose-files/compose.dev.yaml up --build -d
docker exec quatsh-website-web-1 python manage.py collectstatic --noinput
docker exec -it quatsh-website-web-1 sh
dev_restart:
docker compose down
docker compose -f ./compose.yaml -f ./.docker-compose-files/compose.dev.yaml up -d
docker exec -it quatsh-website-web-1 sh
dev_restart_with_logs:
docker compose down
docker compose -f ./compose.yaml -f ./.docker-compose-files/compose.dev.yaml up
test:
docker compose --env-file .env.template -f ./compose.yaml -f ./compose.test.yaml up --build --abort-on-container-exit --exit-code-from web
docker compose --env-file .env.template -f ./compose.yaml -f ./.docker-compose-files/compose.test.yaml up --build --abort-on-container-exit --exit-code-from web

View File

@@ -1,2 +1,65 @@
# Quatsh-Website
## Requirements
### Install make
Running this requires the use of "make" and docker.
"make" tends to come pre-installed on linux but not on Windows, to install it on Windows I recommend using Chocolatey as follows: \
```
choco install make
```
### Install docker
<https://docs.docker.com/get-started/get-docker/>
## Running for the first time
### Env
Before running the container environment has to be setup first, so first run:
```
cp .env.template .env
```
Then make any changes to the .env file.
### Running the website
To run the website 3 options have been provided
#### Development
For development purposes only as the src folder has been mounted in the container to allow for the making of changes without rebuilding the entire image.
Properly utilising this also requires the DEBUG environment variable to be set to TRUE.
```shell
make dev
```
#### Testing
Runs the testing environment and exits.
```shell
make test
```
#### Production
Using `make prod` is identical but preferred here.
```shell
make prod
```
Or
```shell
make
```

View File

@@ -1,21 +1,26 @@
services:
web:
build: .
command: gunicorn --proxy-protocol auto --proxy-allow-from 10.10.50.11 website.wsgi:application
command: gunicorn website.wsgi:application
depends_on:
- db
ports:
- 8000:8000
environment:
ALLOWED_HOSTS: ${NGINX_HOSTNAME}
PYTHONDONTWRITEBYTECODE: 1
PYTHONUNBUFFERED: 1
DJANGO_SETTINGS_MODULE: ${DJANGO_SETTINGS_MODULE}
VIRTUAL_HOST: localhost
#VIRTUAL_PORT: 8000
restart: unless-stopped
volumes:
- static_volume:/app/static
- media_volume:/app/media
db:
image: postgres:18
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_DB: ${POSTGRES_DBNAME}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
@@ -30,6 +35,23 @@ services:
ports:
- 8080:8080
proxy:
image: nginx:stable
volumes:
- ./.nginx/.templates:/etc/nginx/templates
- static_volume:/app/static:ro
- media_volume:/app/media:ro
restart: unless-stopped
ports:
- 80:80
- 443:443
environment:
- NGINX_HOSTNAME=${NGINX_HOSTNAME}
- NGINX_PORT=80
- NGINX_SSL_PORT=443
depends_on:
- web
test:
build: .
command: python manage.py test
@@ -41,3 +63,5 @@ services:
volumes:
postgres_data:
static_volume:
media_volume:

View File

@@ -1,3 +1,5 @@
Django==6.0.3
gunicorn==25.1.0
psycopg [binary] ==3.3.3
django-browser-reload
django-watchfiles

0
src/polls/__init__.py Normal file
View File

7
src/polls/admin.py Normal file
View File

@@ -0,0 +1,7 @@
from django.contrib import admin
# Register your models here.
from .models import Question
admin.site.register(Question)

5
src/polls/apps.py Normal file
View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class PollsConfig(AppConfig):
name = 'polls'

View File

@@ -0,0 +1,32 @@
# Generated by Django 6.0.3 on 2026-03-25 14:26
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Question',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('question_text', models.CharField(max_length=200)),
('pub_date', models.DateTimeField(verbose_name='date published')),
],
),
migrations.CreateModel(
name='Choice',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('choice_text', models.CharField(max_length=200)),
('votes', models.IntegerField(default=0)),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.question')),
],
),
]

View File

20
src/polls/models.py Normal file
View File

@@ -0,0 +1,20 @@
from django.db import models
# Create your models here.
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField("date published")
def __str__(self):
return self.question_text
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text

View File

@@ -0,0 +1,11 @@
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>My test page</title>
</head>
<body>
<h1>Hey This is a test of watchfiles + browser reload!</h1>
{{ question }}
</body>
</html>

View File

@@ -0,0 +1,19 @@
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>My test page</title>
</head>
<body>
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
</body>
</html>

3
src/polls/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

14
src/polls/urls.py Normal file
View File

@@ -0,0 +1,14 @@
from django.urls import path
from . import views
urlpatterns = [
# ex: /polls/
path("", views.index, name="index"),
# ex: /polls/5/
path("<int:question_id>/", views.detail, name="detail"),
# ex: /polls/5/results/
path("<int:question_id>/results/", views.results, name="results"),
# ex: /polls/5/vote/
path("<int:question_id>/vote/", views.vote, name="vote"),
]

24
src/polls/views.py Normal file
View File

@@ -0,0 +1,24 @@
from django.http import HttpResponse, Http404
from django.shortcuts import get_object_or_404, render
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
context = {"latest_question_list": latest_question_list}
return render(request, "polls/index.html", context)
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, "polls/detail.html", {"question": question})
def results(request, question_id):
response = "You're looking at the results of question %s."
return HttpResponse(response % question_id)
def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)

View File

@@ -21,24 +21,27 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-c$q7wdq+u@ow74wp!&zzkxdylkueu)(+34e%!e0du&bjwoqz9z"
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = bool(os.getenv("DEBUG"))
DEBUG = os.getenv("DEBUG") == "TRUE"
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS").split(",")
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "127.0.0.1").split(",")
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
# Application definition
INSTALLED_APPS = [
"polls.apps.PollsConfig",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django_browser_reload",
"django_watchfiles",
]
MIDDLEWARE = [
@@ -49,6 +52,7 @@ MIDDLEWARE = [
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"django_browser_reload.middleware.BrowserReloadMiddleware",
]
ROOT_URLCONF = "website.urls"
@@ -77,7 +81,7 @@ WSGI_APPLICATION = "website.wsgi.application"
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"DBNAME": os.getenv("POSTGRES_DBNAME"),
"NAME": os.getenv("POSTGRES_DBNAME"),
"USER": os.getenv("POSTGRES_USER"),
"PASSWORD": os.getenv("POSTGRES_PASSWORD"),
"HOST": os.getenv("POSTGRES_HOST"),
@@ -110,7 +114,7 @@ AUTH_PASSWORD_VALIDATORS = [
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
TIME_ZONE = "Europe/Amsterdam"
USE_I18N = True
@@ -120,4 +124,5 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/6.0/howto/static-files/
STATIC_ROOT = "/app/static/"
STATIC_URL = "static/"

View File

@@ -14,9 +14,12 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path("__reload__/", include("django_browser_reload.urls")),
path("polls/", include("polls.urls")),
path("admin/", admin.site.urls),
]