Compare commits

..

No commits in common. "8ed93ebc12c35cd884749a22ffbc11b80fd42fe0" and "6464703381a46bbd6ec90d4ca43d6abed7ddb53c" have entirely different histories.

22 changed files with 20 additions and 306 deletions

4
.gitignore vendored
View file

@ -1,9 +1,7 @@
/build /build
*/mediafiles */uploads
*/staticfiles
__pycache__ __pycache__
*.pyc *.pyc
.venv .venv
.env*
*.sqlite3 *.sqlite3

View file

@ -2,7 +2,7 @@
Web backend for hosting a wedding website. Web backend for hosting a wedding website.
# Run Local server: ## Run development server:
```sh ```sh
source .venv/bin/activate source .venv/bin/activate
@ -10,49 +10,10 @@ cd wedding_site
python manage.py runserver python manage.py runserver
``` ```
# Do Migrations ## Do Migrations
```sh ```sh
cd wedding_site cd wedding_site
python manage.py makemigrations primary python manage.py makemigrations primary
python manage.py migrate python manage.py migrate
``` ```
# Run Dev Server in container
```sh
podman-compose -f compose.yaml up -d --build
```
Bring it down with:
```sh
podman-compose down -v
```
# Run Prod Server
```sh
podman-compose -f compose.prod.yaml up -d --build
```
Sync static files:
```sh
podman-compose -f compose.prod.yaml exec web python manage.py collectstatic --no-input --clear
```
Check cert
```sh
podman-compose -f compose.prod.yaml exec acme-companion /app/cert_status
```
Force renew cert:
```sh
podman-compose -f compose.prod.yaml exec acme-companion /app/force_renew
```

View file

@ -1,52 +0,0 @@
version: '3.8'
services:
web:
build: ./wedding_site
command: gunicorn wedding_site.wsgi:application --bind 0.0.0.0:8000
volumes:
- ./wedding_site/:/usr/src/app/
- static_volume:/staticfiles
- media_volume:/mediafiles
expose:
- 8000
env_file:
- ./.env.prod
nginx-proxy:
container_name: nginx-proxy
build: ./nginx
restart: always
ports:
- 443:443
- 80:80
volumes:
- static_volume:/staticfiles
- media_volume:/mediafiles
- certs:/etc/nginx/certs
- html:/usr/share/nginx/html
- vhost:/etc/nginx/vhost.d
- /var/run/docker.sock:/tmp/docker.sock:ro
depends_on:
- web
acme-companion:
image: docker.io/nginxproxy/acme-companion
env_file:
- ./.env.prod.proxy-companion
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- certs:/etc/nginx/certs
- html:/usr/share/nginx/html
- vhost:/etc/nginx/vhost.d
- acme:/etc/acme.sh
depends_on:
- nginx-proxy
volumes:
static_volume:
media_volume:
certs:
html:
vhost:
acme:

View file

@ -1,52 +0,0 @@
version: '3.8'
services:
web:
build: ./wedding_site
command: gunicorn wedding_site.wsgi:application --bind 0.0.0.0:8000
volumes:
- ./wedding_site/:/usr/src/app/
- static_volume:/staticfiles
- media_volume:/mediafiles
expose:
- 8000
env_file:
- ./.env.staging
nginx-proxy:
container_name: nginx-proxy
build: ./nginx
restart: always
ports:
- 443:443
- 80:80
volumes:
- static_volume:/staticfiles
- media_volume:/mediafiles
- certs:/etc/nginx/certs
- html:/usr/share/nginx/html
- vhost:/etc/nginx/vhost.d
- /var/run/docker.sock:/tmp/docker.sock:ro
depends_on:
- web
acme-companion:
image: docker.io/nginxproxy/acme-companion
env_file:
- ./.env.staging.proxy-companion
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- certs:/etc/nginx/certs
- html:/usr/share/nginx/html
- vhost:/etc/nginx/vhost.d
- acme:/etc/acme.sh
depends_on:
- nginx-proxy
volumes:
static_volume:
media_volume:
certs:
html:
vhost:
acme:

View file

@ -1,12 +0,0 @@
version: '3.8'
services:
web:
build: ./wedding_site
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./wedding_site/:/usr/src/app/
ports:
- 8000:8000
env_file:
- ./.env.dev

View file

@ -1 +0,0 @@
pacman -Sy podman aardvark-dns

View file

@ -1,4 +0,0 @@
FROM docker.io/nginxproxy/nginx-proxy
COPY vhost.d/default /etc/nginx/vhost.d/default
COPY custom.conf /etc/nginx/conf.d/custom.conf

View file

@ -1 +0,0 @@
client_max_body_size 100M;

View file

@ -1,9 +0,0 @@
location /static/ {
alias /staticfiles/;
add_header Access-Control-Allow-Origin *;
}
location /media/ {
alias /mediafiles/;
add_header Access-Control-Allow-Origin *;
}

View file

@ -1 +1,4 @@
podman-compose django
markdown
pillow
beautifulsoup4

View file

@ -1,19 +0,0 @@
# pull official base image
FROM python:3.11.4-slim-buster
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
RUN mkdir staticfiles
RUN mkdir mediafiles
ADD ./mediafiles /mediafiles
# copy project
COPY . .

View file

@ -1,18 +0,0 @@
# Generated by Django 5.0.2 on 2024-02-18 09:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('primary', '0009_page_pretty_name'),
]
operations = [
migrations.AddField(
model_name='page',
name='prority',
field=models.IntegerField(default=0),
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 5.0.2 on 2024-02-18 10:02
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('primary', '0010_page_prority'),
]
operations = [
migrations.RenameField(
model_name='page',
old_name='prority',
new_name='priority',
),
]

View file

@ -18,7 +18,6 @@ class Page(models.Model):
pretty_name = models.CharField(max_length=200, default="") pretty_name = models.CharField(max_length=200, default="")
published = models.BooleanField(default=False) published = models.BooleanField(default=False)
navigable = models.BooleanField(default=False) navigable = models.BooleanField(default=False)
priority = models.IntegerField(default=0)
def __str__(self): def __str__(self):
return self.name return self.name

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -33,9 +33,9 @@ body {
img { img {
margin:0 auto; margin:0 auto;
width: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
max-width:100%;
max-height:300px; max-height:300px;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 4px 8px 0; box-shadow: 0 4px 8px 0;
@ -107,10 +107,6 @@ ul {
list-style-position: inside; list-style-position: inside;
} }
ul li {
padding: 5px 0px;
}
.login-input{ .login-input{
margin-top: 30px; margin-top: 30px;
text-align: center; text-align: center;
@ -147,7 +143,7 @@ input[type=submit] {
margin-top: 30px; margin-top: 30px;
text-align: center; text-align: center;
font-size: 20px; font-size: 20px;
width: 75%; width: 50%;
margin:0 auto; margin:0 auto;
font-family: GlacialIndifference; font-family: GlacialIndifference;
} }

View file

@ -3,5 +3,4 @@
content="width=device-width, initial-scale=1"> content="width=device-width, initial-scale=1">
{% load static %} {% load static %}
<link rel="shortcut icon" type="image/png" href="{% static 'primary/favicon.ico' %}"/>
<link rel="stylesheet" href="{% static 'primary/style.css' %}"> <link rel="stylesheet" href="{% static 'primary/style.css' %}">

View file

@ -7,7 +7,6 @@ urlpatterns = [
path("", views.index, name="index"), path("", views.index, name="index"),
path('login', views.login_view, name="login"), path('login', views.login_view, name="login"),
path("home", views.home, name="home"), path("home", views.home, name="home"),
path("afters", views.afters, name="afters"),
path("schedule", views.schedule, name="schedule"), path("schedule", views.schedule, name="schedule"),
path("thingstodo", views.things_to_do, name="thingstodo"), path("thingstodo", views.things_to_do, name="thingstodo"),
path("travel", views.travel, name="travel"), path("travel", views.travel, name="travel"),

View file

@ -6,8 +6,6 @@ from django.template import Context
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.views.defaults import page_not_found
import markdown import markdown
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
@ -26,17 +24,6 @@ _TEMPLATE = """
</html> </html>
""" """
def handler404(request, exception, template_name="404.html"):
page_names = ["home", "schedule", "thingstodo", "afters"]
working_path = request.path.lower()
if working_path and working_path[0] == "/":
working_path = working_path[1:]
if working_path and working_path[-1] == "/":
working_path = working_path[:-1]
if working_path in page_names:
return redirect(working_path)
return page_not_found(request, exception, template_name)
def get_site_header(site): def get_site_header(site):
template = Template(site.header) template = Template(site.header)
context = Context({"site": site}) context = Context({"site": site})
@ -59,11 +46,8 @@ def index(request):
return HttpResponse(soup.prettify()) return HttpResponse(soup.prettify())
def login_view(request): def login_view(request):
if request.method == "GET": token = request.POST["token"]
token = request.GET["token"] user = authenticate(request, username="guest", password=token)
else:
token = request.POST["token"]
user = authenticate(request, username="guest", password=token.lower())
if user is not None: if user is not None:
login(request, user) login(request, user)
return redirect("home") return redirect("home")
@ -74,13 +58,9 @@ def login_view(request):
def home(request): def home(request):
return get_page("Home") return get_page("Home")
@login_required(login_url="/")
def afters(request):
return get_page("Afters")
def get_page_header(site: Site): def get_page_header(site: Site):
pages = site.page_set.order_by("priority").filter(navigable=True) pages = site.page_set.filter(navigable=True)
template = Template(site.page_header) template = Template(site.page_header)
context = Context({"pages" : pages}) context = Context({"pages" : pages})
return template.render(context) return template.render(context)
@ -107,8 +87,7 @@ def get_page(name:str):
img_name = img["src"] img_name = img["src"]
db_images = Image.objects.filter(name=img_name) db_images = Image.objects.filter(name=img_name)
if db_images: if db_images:
url = db_images[0].content.url.replace("uploads/","") img["src"] = db_images[0].content.url
img["src"] = url
return HttpResponse(soup.prettify()) return HttpResponse(soup.prettify())

View file

@ -1,5 +0,0 @@
django
markdown
pillow
beautifulsoup4
gunicorn

View file

@ -11,7 +11,6 @@ https://docs.djangoproject.com/en/5.0/ref/settings/
""" """
from pathlib import Path from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
@ -20,15 +19,13 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
SECRET_KEY = os.environ.get("SECRET_KEY") # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-bj9fez3qztt5e2lrzpgh%%nat@w^kn!k@l92l=+#%wm)4)p^5m'
if "DJANGO_DEBUG" in os.environ: # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.environ.get("DJANGO_DEBUG") == 1 DEBUG = True
else:
DEBUG=False
if "DJANGO_ALLOWED_HOSTS" in os.environ: ALLOWED_HOSTS = []
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS").split(" ")
# Application definition # Application definition
@ -115,29 +112,12 @@ USE_I18N = True
USE_TZ = True USE_TZ = True
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
if "CSRF_TRUSTED_ORIGINS" in os.environ:
CSRF_TRUSTED_ORIGINS = os.environ.get("CSRF_TRUSTED_ORIGINS").split(" ")
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/ # https://docs.djangoproject.com/en/5.0/howto/static-files/
STATIC_URL = 'static/' STATIC_URL = 'static/'
if "DJANGO_STATIC_ROOT" in os.environ:
STATIC_ROOT = os.environ.get("DJANGO_STATIC_ROOT")
else:
STATIC_ROOT = BASE_DIR / "staticfiles"
MEDIA_URL = 'media/'
if "DJANGO_MEDIA_ROOT" in os.environ:
MEDIA_ROOT = os.environ.get("DJANGO_MEDIA_ROOT")
else:
MEDIA_ROOT = BASE_DIR / "mediafiles"
# Default primary key field type # Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field

View file

@ -19,16 +19,7 @@ from django.urls import path, include
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
import os
if "DJANGO_ADMIN_PATH" in os.environ:
ADMIN_URL = os.environ.get("DJANGO_ADMIN_PATH") + "/"
else:
ADMIN_URL = "admin/"
urlpatterns = [ urlpatterns = [
path("", include('primary.urls')), path("", include('primary.urls')),
path(ADMIN_URL, admin.site.urls), path('admin/', admin.site.urls),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
handler404 = 'primary.views.handler404'