Compare commits

...

16 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
WGAVermeer
1c545638b9 run deploy check aswell. 2026-03-11 09:26:45 +01:00
22 changed files with 290 additions and 28 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,6 @@ run-name: ${{ gitea.actor }}
on:
push:
branches:
- pre-prod
- main
pull_request:
@@ -16,3 +15,6 @@ jobs:
- name: Build and test
run: make test
- name: Build
run: make prod

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

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,11 +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 -d
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,44 +1,65 @@
# Quatsh-Website
## Requirements:
## 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/
<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 nessecary adjustments to the .env file.
### Running the website:
Then make any changes to the .env file.
### Running the website
To run the website 3 options have been provided
#### Development:
#### 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
```
Or
```shell
make
```
```

View File

@@ -1,16 +1,21 @@
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
@@ -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

View File

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

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

@@ -1,3 +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>

View File

@@ -3,5 +3,12 @@ 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"),
]

View File

@@ -1,9 +1,24 @@
from django.shortcuts import render
from django.http import HttpResponse
from django.http import HttpResponse, Http404
from django.shortcuts import get_object_or_404, render
from .models import Question
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
latest_question_list = Question.objects.order_by("-pub_date")[:5]
context = {"latest_question_list": latest_question_list}
return render(request, "polls/index.html", context)
# Create your views here.
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

@@ -24,21 +24,24 @@ BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = bool(os.getenv("DEBUG", False))
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"
@@ -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

@@ -19,6 +19,7 @@ from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("__reload__/", include("django_browser_reload.urls")),
path("polls/", include("polls.urls")),
path("admin/", admin.site.urls),
]