Compare commits
9 Commits
a0a0cfab8a
...
pre-prod
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99c0b4ae6d | ||
|
|
33661e14da | ||
|
|
bef2577746 | ||
|
|
2b5f28d8f1 | ||
|
|
a06026cb37 | ||
|
|
5c132f5d37 | ||
|
|
7db860a4eb | ||
|
|
6d6534a541 | ||
|
|
358bd4cbf2 |
@@ -1,18 +1,36 @@
|
|||||||
|
---
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
command: gunicorn --capture-output --enable-stdio-inheritance -b 0.0.0.0:8000 website.wsgi:application
|
command: python manage.py runserver 0.0.0.0:8000
|
||||||
volumes:
|
volumes: [./src:/src]
|
||||||
- ./src:/src
|
|
||||||
env_file:
|
env_file:
|
||||||
- path: .env.template
|
- path: .env.template
|
||||||
required: true
|
required: true
|
||||||
- path: .env
|
- path: .env
|
||||||
required: false
|
required: false
|
||||||
|
environment:
|
||||||
|
DJANGO_RELOAD: true
|
||||||
|
DEBUG: true
|
||||||
|
ALLOWED_HOSTS: localhost,127.0.0.1
|
||||||
|
ports: [127.0.0.1:8000:8000]
|
||||||
db:
|
db:
|
||||||
env_file:
|
env_file:
|
||||||
- path: .env.template
|
- path: .env.template
|
||||||
required: true
|
required: true
|
||||||
- path: .env
|
- path: .env
|
||||||
required: false
|
required: false
|
||||||
|
proxy:
|
||||||
|
restart: unless-stopped
|
||||||
|
ports: [127.0.0.1:80:80]
|
||||||
|
environment: [NGINX_HOSTNAME=localhost, NGINX_PORT=80]
|
||||||
|
tailwind:
|
||||||
|
image: python:3.14-slim
|
||||||
|
working_dir: /src
|
||||||
|
command: >
|
||||||
|
sh -c "apt-get update && apt-get install -y curl &&
|
||||||
|
curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64
|
||||||
|
-o /usr/local/bin/tailwindcss &&
|
||||||
|
chmod +x /usr/local/bin/tailwindcss &&
|
||||||
|
tailwindcss -i ./static/css/input.css -o ./static/css/output.css --watch"
|
||||||
|
volumes: [.:/src]
|
||||||
|
networks: [frontend]
|
||||||
|
|||||||
@@ -1,10 +1,25 @@
|
|||||||
|
---
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
|
command: gunicorn website.wsgi:application
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
ALLOWED_HOSTS: ${NGINX_HOSTNAME}
|
||||||
|
DJANGO_SETTINGS_MODULE: ${DJANGO_SETTINGS_MODULE}
|
||||||
env_file:
|
env_file:
|
||||||
- path: .env
|
- path: .env
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
db:
|
db:
|
||||||
|
restart: unless-stopped
|
||||||
env_file:
|
env_file:
|
||||||
- path: .env
|
- path: .env
|
||||||
required: true
|
required: true
|
||||||
|
adminer:
|
||||||
|
restart: unless-stopped
|
||||||
|
proxy:
|
||||||
|
restart: unless-stopped
|
||||||
|
ports: [80:80, 443:443]
|
||||||
|
environment:
|
||||||
|
- NGINX_HOSTNAME=${NGINX_HOSTNAME}
|
||||||
|
- NGINX_PORT=80
|
||||||
|
- NGINX_SSL_PORT=443
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
|
---
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
command: python manage.py check --deploy; python -Wa manage.py test --noinput --parallel
|
command: python manage.py check --deploy; python -Wa manage.py test --noinput
|
||||||
restart: "no"
|
--parallel
|
||||||
|
environment:
|
||||||
|
DEBUG: false
|
||||||
|
DJANGO_SETTINGS_MODULE: website.settings.test
|
||||||
env_file:
|
env_file:
|
||||||
- path: .env.template
|
- path: .env.template
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
|
||||||
db:
|
db:
|
||||||
|
# Tmpfs keeps tests fast and isolated — no persistent volume needed
|
||||||
|
tmpfs: [/var/lib/postgresql/data]
|
||||||
env_file:
|
env_file:
|
||||||
- path: .env.template
|
- path: .env.template
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ NGINX_HOSTNAME=localhost
|
|||||||
# Django
|
# Django
|
||||||
DJANGO_SETTINGS_MODULE=website.settings
|
DJANGO_SETTINGS_MODULE=website.settings
|
||||||
DJANGO_SECRET_KEY=CWHZCAZBNV57tDkwGHJwTUu3PCSnGG45
|
DJANGO_SECRET_KEY=CWHZCAZBNV57tDkwGHJwTUu3PCSnGG45
|
||||||
DEBUG=TRUE
|
|
||||||
#ALLOWED_HOSTS=localhost
|
|
||||||
|
|
||||||
# Database (PostgreSQL)
|
# Database (PostgreSQL)
|
||||||
POSTGRES_USER=test_user
|
POSTGRES_USER=test_user
|
||||||
@@ -17,4 +15,4 @@ POSTGRES_DB=test_db
|
|||||||
|
|
||||||
# Gunicorn
|
# Gunicorn
|
||||||
GUNICORN_WORKERS=3
|
GUNICORN_WORKERS=3
|
||||||
GUNICORN_TIMEOUT=120
|
GUNICORN_TIMEOUT=120
|
||||||
|
|||||||
@@ -1,38 +1,30 @@
|
|||||||
|
---
|
||||||
name: Deploy to production
|
name: Deploy to production
|
||||||
run-name: deploy-${{ gitea.actor }}
|
run-name: deploy-${{ gitea.actor }}
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [main]
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup SSH
|
- name: Setup SSH
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.ssh
|
mkdir -p ~/.ssh
|
||||||
echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_rsa
|
echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_rsa
|
||||||
chmod 600 ~/.ssh/id_rsa
|
chmod 600 ~/.ssh/id_rsa
|
||||||
ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
|
ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
run: |
|
run: |-
|
||||||
ssh -i ~/.ssh/id_rsa ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} << 'EOF'
|
ssh -i ~/.ssh/id_rsa ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} << 'EOF'
|
||||||
cd ~/Quatsh-Website
|
cd ~/Quatsh-Website
|
||||||
|
|
||||||
echo "Pulling latest code..."
|
echo "Pulling latest code..."
|
||||||
git pull origin main
|
git pull origin main
|
||||||
|
|
||||||
echo "Building containers..."
|
echo "Building containers..."
|
||||||
make prod
|
make prod
|
||||||
|
|
||||||
echo "Running Django deploy check"
|
echo "Running Django deploy check"
|
||||||
docker compose run --rm web python manage.py check --deploy
|
docker compose run --rm web python manage.py check --deploy
|
||||||
|
|
||||||
echo "Deployment complete."
|
echo "Deployment complete."
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
|
---
|
||||||
name: Gitea Test.
|
name: Gitea Test.
|
||||||
run-name: ${{ gitea.actor }}
|
run-name: ${{ gitea.actor }}
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [main, pre-prod]
|
||||||
- main
|
|
||||||
- pre-prod
|
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
- name: lint
|
||||||
|
run: make lint
|
||||||
- name: test
|
- name: test
|
||||||
run: make test
|
run: make test
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -178,4 +178,5 @@ cython_debug/
|
|||||||
# gunicon webserver
|
# gunicon webserver
|
||||||
gunicorn.ctl
|
gunicorn.ctl
|
||||||
|
|
||||||
|
# tailwind stuff
|
||||||
|
output.css
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM python:3.14-alpine
|
FROM python:3.14-slim
|
||||||
|
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
PYTHONUNBUFFERED=1
|
PYTHONUNBUFFERED=1
|
||||||
@@ -12,4 +12,11 @@ RUN pip install --upgrade pip && \
|
|||||||
COPY gunicorn.conf.py /src/
|
COPY gunicorn.conf.py /src/
|
||||||
COPY ./src/ /src/
|
COPY ./src/ /src/
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends curl && \
|
||||||
|
curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 -o /usr/local/bin/tailwindcss && \
|
||||||
|
chmod +x /usr/local/bin/tailwindcss && \
|
||||||
|
apt-get remove -y curl && apt-get autoremove -y && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN tailwindcss build -i ./static/css/input.css -o ./static/css/output.css --minify
|
||||||
|
|
||||||
CMD ["gunicorn", "website.wsgi:application"]
|
CMD ["gunicorn", "website.wsgi:application"]
|
||||||
|
|||||||
25
compose.yaml
25
compose.yaml
@@ -2,16 +2,9 @@
|
|||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
build: .
|
build: .
|
||||||
command: gunicorn website.wsgi:application
|
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
environment:
|
|
||||||
ALLOWED_HOSTS: ${NGINX_HOSTNAME}
|
|
||||||
DJANGO_SETTINGS_MODULE: ${DJANGO_SETTINGS_MODULE}
|
|
||||||
# VIRTUAL_HOST: localhost
|
|
||||||
# VIRTUAL_PORT: 8000
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes: [static_volume:/app/static, media_volume:/app/media]
|
volumes: [static_volume:/app/static, media_volume:/app/media]
|
||||||
networks: [frontend, backend]
|
networks: [frontend, backend]
|
||||||
db:
|
db:
|
||||||
@@ -21,32 +14,24 @@ services:
|
|||||||
POSTGRES_USER: ${POSTGRES_USER}
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
volumes: [postgres_data:/var/lib/postgresql]
|
volumes: [postgres_data:/var/lib/postgresql]
|
||||||
restart: unless-stopped
|
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: [CMD-SHELL, 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DBNAME}']
|
test: [CMD-SHELL, 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DBNAME}']
|
||||||
interval: 5s
|
interval: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
networks: [backend]
|
networks: [backend]
|
||||||
adminer:
|
|
||||||
image: adminer
|
|
||||||
depends_on: [db]
|
|
||||||
restart: always
|
|
||||||
ports: [127.0.0.1:8080:8080]
|
|
||||||
networks: [backend]
|
|
||||||
proxy:
|
proxy:
|
||||||
image: nginx:stable
|
image: nginx:stable
|
||||||
volumes:
|
volumes:
|
||||||
- ./.nginx/.templates:/etc/nginx/templates
|
- ./.nginx/.templates:/etc/nginx/templates
|
||||||
- static_volume:/app/static:ro
|
- static_volume:/app/static:ro
|
||||||
- media_volume:/app/media: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]
|
depends_on: [web]
|
||||||
networks: [frontend]
|
networks: [frontend]
|
||||||
|
adminer:
|
||||||
|
image: adminer
|
||||||
|
depends_on: [db]
|
||||||
|
ports: [127.0.0.1:8080:8080]
|
||||||
|
networks: [backend]
|
||||||
networks:
|
networks:
|
||||||
frontend:
|
frontend:
|
||||||
backend:
|
backend:
|
||||||
|
|||||||
0
src/core/__init__.py
Normal file
0
src/core/__init__.py
Normal file
3
src/core/admin.py
Normal file
3
src/core/admin.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
5
src/core/apps.py
Normal file
5
src/core/apps.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class CoreConfig(AppConfig):
|
||||||
|
name = 'core'
|
||||||
0
src/core/migrations/__init__.py
Normal file
0
src/core/migrations/__init__.py
Normal file
3
src/core/models.py
Normal file
3
src/core/models.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
BIN
src/core/static/core/img/5x5-2023-11-16-at-14.56.56-1.jpeg
Normal file
BIN
src/core/static/core/img/5x5-2023-11-16-at-14.56.56-1.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 168 KiB |
13
src/core/templates/core/index.html
Normal file
13
src/core/templates/core/index.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block title %}Home{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h1>Welcome</h1>
|
||||||
|
<div>
|
||||||
|
{% include "components/carousel.html" with carousel_id="hero" carousel_images=carousel_images %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
3
src/core/tests.py
Normal file
3
src/core/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
9
src/core/urls.py
Normal file
9
src/core/urls.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
app_name = "core"
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path("", views.index, name="index"),
|
||||||
|
]
|
||||||
10
src/core/views.py
Normal file
10
src/core/views.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
from django.templatetags.static import static
|
||||||
|
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"core/index.html",
|
||||||
|
{"carousel_images": [static("/core/img/5x5-2023-11-16-at-14.56.56-1.jpeg")]},
|
||||||
|
)
|
||||||
4
src/gallery/templates/gallery/index.html
Normal file
4
src/gallery/templates/gallery/index.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% block title %}Home{% endblock %}
|
||||||
|
{% block content %}<h1>Welcome</h1>{% endblock %}
|
||||||
9
src/gallery/urls.py
Normal file
9
src/gallery/urls.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
app_name = "gallery"
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path("", views.index, name="index"),
|
||||||
|
]
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
|
||||||
# Create your views here.
|
|
||||||
|
def index(request):
|
||||||
|
return render(request, "gallery/index.html")
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ from django.urls import path
|
|||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
app_name = "polls"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# ex: /polls/
|
# ex: /polls/
|
||||||
path("", views.index, name="index"),
|
path("", views.index, name="index"),
|
||||||
|
|||||||
1
src/static/css/input.css
Normal file
1
src/static/css/input.css
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@import "tailwindcss"
|
||||||
68
src/static/js/carousel.js
Normal file
68
src/static/js/carousel.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
function initCarousel(id) {
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
const track = document.getElementById(id);
|
||||||
|
const originalSlides = Array.from(track.children);
|
||||||
|
const total = originalSlides.length;
|
||||||
|
|
||||||
|
if (total <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstClone = originalSlides[0].cloneNode(true);
|
||||||
|
const lastClone = originalSlides[total - 1].cloneNode(true);
|
||||||
|
track.appendChild(firstClone);
|
||||||
|
track.insertBefore(lastClone, originalSlides[0]);
|
||||||
|
|
||||||
|
let current = 1;
|
||||||
|
let isTransitioning = false;
|
||||||
|
track.style.transform = `translateX(-${current * 100}%)`;
|
||||||
|
|
||||||
|
const dots = document.querySelectorAll(`.${id}_dot`);
|
||||||
|
|
||||||
|
function updateDots(index) {
|
||||||
|
const realIndex = (index - 1 + total) % total;
|
||||||
|
dots.forEach((d, i) => {
|
||||||
|
d.classList.toggle("opacity-100", i === realIndex);
|
||||||
|
d.classList.toggle("opacity-50", i !== realIndex);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function goTo(index) {
|
||||||
|
if (isTransitioning) return;
|
||||||
|
isTransitioning = true;
|
||||||
|
track.style.transition = "transform 500ms ease-in-out";
|
||||||
|
current = index;
|
||||||
|
track.style.transform = `translateX(-${current * 100}%)`;
|
||||||
|
updateDots(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
goTo(current + 1);
|
||||||
|
}
|
||||||
|
function prev() {
|
||||||
|
goTo(current - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
track.addEventListener("transitionend", function () {
|
||||||
|
if (current === 0) {
|
||||||
|
track.style.transition = "none";
|
||||||
|
current = total;
|
||||||
|
track.style.transform = `translateX(-${current * 100}%)`;
|
||||||
|
}
|
||||||
|
if (current === total + 1) {
|
||||||
|
track.style.transition = "none";
|
||||||
|
current = 1;
|
||||||
|
track.style.transform = `translateX(-${current * 100}%)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
isTransitioning = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
setInterval(next, 5000);
|
||||||
|
updateDots(current);
|
||||||
|
|
||||||
|
window[`${id}_goTo`] = (i) => goTo(i + 1);
|
||||||
|
window[`${id}_next`] = next;
|
||||||
|
window[`${id}_prev`] = prev;
|
||||||
|
});
|
||||||
|
}
|
||||||
18
src/templates/base.html
Normal file
18
src/templates/base.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>
|
||||||
|
{% block title %}My Site{% endblock %}
|
||||||
|
</title>
|
||||||
|
<link rel="stylesheet" href="{% static 'css/output.css' %}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% include "components/topbar.html" %}
|
||||||
|
<main>
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
28
src/templates/components/carousel.html
Normal file
28
src/templates/components/carousel.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{% load static %}
|
||||||
|
<div class="relative w-full overflow-hidden">
|
||||||
|
<div id="{{ carousel_id }}" class="flex w-full">
|
||||||
|
{% for image in carousel_images %}
|
||||||
|
<div class="w-full flex-shrink-0">
|
||||||
|
<img src="{{ image }}" class="w-full object-cover">
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% if carousel_images|length > 1 %}
|
||||||
|
<button onclick="{{ carousel_id }}_prev()"
|
||||||
|
class="absolute left-2 top-1/2 -translate-y-1/2 bg-black/50 text-white px-3 py-1 rounded-full">
|
||||||
|
←
|
||||||
|
</button>
|
||||||
|
<button onclick="{{ carousel_id }}_next()"
|
||||||
|
class="absolute right-2 top-1/2 -translate-y-1/2 bg-black/50 text-white px-3 py-1 rounded-full">
|
||||||
|
→
|
||||||
|
</button>
|
||||||
|
<div class="absolute bottom-2 w-full flex justify-center gap-2">
|
||||||
|
{% for image in carousel_images %}
|
||||||
|
<span class="{{ carousel_id }}_dot w-2 h-2 rounded-full bg-white opacity-50 cursor-pointer"
|
||||||
|
onclick="{{ carousel_id }}_goTo({{ forloop.counter0 }})"></span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<script src="{% static 'js/carousel.js' %}"></script>
|
||||||
|
<script>initCarousel('{{ carousel_id }}');</script>
|
||||||
8
src/templates/components/topbar.html
Normal file
8
src/templates/components/topbar.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{% url 'core:index' %}">Home</a></li>
|
||||||
|
<li><a href="{% url 'polls:index' %}">Polls</a></li>
|
||||||
|
<li><a href="{% url 'gallery:index' %}">Gallery</a></li>
|
||||||
|
</ul>
|
||||||
|
<!-- rest of your topbar -->
|
||||||
|
</nav>
|
||||||
@@ -10,8 +10,8 @@ For the full list of settings and their values, see
|
|||||||
https://docs.djangoproject.com/en/6.0/ref/settings/
|
https://docs.djangoproject.com/en/6.0/ref/settings/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
# 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
|
||||||
@@ -24,7 +24,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
|||||||
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
|
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = os.getenv("DEBUG") == "TRUE"
|
DEBUG = os.getenv("DEBUG", "false") == "true"
|
||||||
|
|
||||||
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "127.0.0.1").split(",")
|
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "127.0.0.1").split(",")
|
||||||
|
|
||||||
@@ -34,6 +34,8 @@ SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
|||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
"polls.apps.PollsConfig",
|
"polls.apps.PollsConfig",
|
||||||
|
"core.apps.CoreConfig",
|
||||||
|
"gallery.apps.GalleryConfig",
|
||||||
"django.contrib.admin",
|
"django.contrib.admin",
|
||||||
"django.contrib.auth",
|
"django.contrib.auth",
|
||||||
"django.contrib.contenttypes",
|
"django.contrib.contenttypes",
|
||||||
@@ -53,7 +55,7 @@ MIDDLEWARE = [
|
|||||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
]
|
]
|
||||||
|
|
||||||
if os.getenv("DJANGO_RELOAD", "False") == "True":
|
if os.getenv("DJANGO_RELOAD", "false") == "true":
|
||||||
INSTALLED_APPS.append("django_browser_reload")
|
INSTALLED_APPS.append("django_browser_reload")
|
||||||
INSTALLED_APPS.append("django_watchfiles")
|
INSTALLED_APPS.append("django_watchfiles")
|
||||||
MIDDLEWARE.append("django_browser_reload.middleware.BrowserReloadMiddleware")
|
MIDDLEWARE.append("django_browser_reload.middleware.BrowserReloadMiddleware")
|
||||||
@@ -63,7 +65,7 @@ ROOT_URLCONF = "website.urls"
|
|||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
"DIRS": [],
|
"DIRS": [BASE_DIR / "templates"],
|
||||||
"APP_DIRS": True,
|
"APP_DIRS": True,
|
||||||
"OPTIONS": {
|
"OPTIONS": {
|
||||||
"context_processors": [
|
"context_processors": [
|
||||||
@@ -129,3 +131,7 @@ USE_TZ = True
|
|||||||
|
|
||||||
STATIC_ROOT = "/app/static/"
|
STATIC_ROOT = "/app/static/"
|
||||||
STATIC_URL = "static/"
|
STATIC_URL = "static/"
|
||||||
|
|
||||||
|
STATICFILES_DIRS = [
|
||||||
|
BASE_DIR / "static",
|
||||||
|
]
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ from django.contrib import admin
|
|||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path("", include("core.urls")),
|
||||||
path("polls/", include("polls.urls")),
|
path("polls/", include("polls.urls")),
|
||||||
|
path("gallery/", include("gallery.urls")),
|
||||||
path("admin/doc/", include("django.contrib.admindocs.urls")),
|
path("admin/doc/", include("django.contrib.admindocs.urls")),
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
]
|
]
|
||||||
|
|
||||||
if os.getenv("DJANGO_RELOAD", "False") == "True":
|
if os.getenv("DJANGO_RELOAD", "false") == "true":
|
||||||
urlpatterns.append(path("__reload__/", include("django_browser_reload.urls")))
|
urlpatterns.append(path("__reload__/", include("django_browser_reload.urls")))
|
||||||
|
|||||||
Reference in New Issue
Block a user