Fuck so much to comment on

Renamed the folders and containers to something more reasonable
Using .env file for secretes so I can better hide them from git
Mostly it, I think
This commit is contained in:
Michał Gdula 2023-06-09 22:27:30 +03:00
parent a29f06dfee
commit a4ebfa8552
61 changed files with 84 additions and 111 deletions

168
GameExpo/.gitignore vendored Normal file
View file

@ -0,0 +1,168 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
# remove development files
/instance
/migrations
/storage
/logs
/website/static/.webpack-cache
/website/static/gen

16
GameExpo/Dockerfile Normal file
View file

@ -0,0 +1,16 @@
# syntax=docker/dockerfile:1
FROM alpine:latest
EXPOSE 5000
RUN apk update && apk add python3 py3-pip
WORKDIR /data
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
RUN mkdir /storage
COPY ./website ./website
COPY ./run.sh ./run.sh
RUN chmod +x ./run.sh
CMD ["./run.sh"]

24
GameExpo/README.md Normal file
View file

@ -0,0 +1,24 @@
# Game Expo 2023
The all new and upgraded website that runs on the Flask macro-framework
## Developing
### Prerequisites
Clone the latest branch, and create a venv environment. Then, install the requirements.txt file.
To create the required database run:
```bash
$ flask --app website init-db
```
### Running
To run the website, run the following command:
```bash
$ flask --app website run
```
## Running
While the docker-compose file runs the program for you, its possible to just run the file locally.
```bash
# gunicorn --bind="0.0.0.0:5000" --workers=4 website:app
```

12
GameExpo/requirements.txt Normal file
View file

@ -0,0 +1,12 @@
Gunicorn
Flask
Flask-SQLAlchemy
Flask-Migrate
Flask-Login
WTForms
Flask-WTF
Flask-Assets
Flask-Caching
libsass-bin
jsmin
cssmin

20
GameExpo/run.sh Normal file
View file

@ -0,0 +1,20 @@
#!/bin/sh
# Check if migrastions folder exists
if [ ! -d "/data/storage/migrations" ];
then
echo "Creating tables..."
flask --app website db init
fi
# Check if there are any changes to the database
if ! $(flask --app website db check) | grep -q "No changes in schema detected.";
then
echo "Database changes detected! Migrating..."
flask --app website db migrate
flask --app website db upgrade
fi
# Start website!!!!
echo "Starting expo website..."
gunicorn --bind expo:5000 website:app

View file

@ -0,0 +1,35 @@
from flask import Flask
from flask_assets import Bundle
from website.models import Users
from website.extensions import db, migrate, login_manager, assets
from website.config import INSTANCE_DIR, MIGRATION_DIR
from website import routes
app = Flask(__name__, instance_path=INSTANCE_DIR)
app.config.from_pyfile("config.py")
db.init_app(app)
migrate.init_app(app, db, directory=MIGRATION_DIR)
with app.app_context():
db.create_all()
login_manager.init_app(app)
assets.init_app(app)
styles = Bundle(
"sass/styles.sass",
filters="libsass, cssmin",
output="gen/packed.css",
depends="sass/*.sass",
)
assets.register("styles", styles)
scripts = Bundle("js/*.js", filters="jsmin", output="gen/packed.js")
assets.register("scripts", scripts)
app.register_blueprint(routes.blueprint)
@login_manager.user_loader
def load_user(user_id):
return Users.query.filter_by(id=user_id).first()

View file

@ -0,0 +1,15 @@
import os
# Purely to make the code a bit more readable
def env(key):
return os.getenv(key)
# SECRET_KEY = env("FLASK_KEY")
SECRET_KEY = "dev"
SQLALCHEMY_DATABASE_URI = "sqlite:///site.db"
MIGRATION_DIR = "/data/storage/migrations"
INSTANCE_DIR = "/data/storage/instance"

View file

@ -0,0 +1,11 @@
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_caching import Cache
from flask_assets import Environment
db = SQLAlchemy()
migrate = Migrate()
login_manager = LoginManager()
cache = Cache(config={"CACHE_TYPE": "simple"})
assets = Environment()

View file

@ -0,0 +1,54 @@
"""
Database models for the server
"""
from website.extensions import db
from flask_login import UserMixin
class Images(db.Model):
id = db.Column(db.Integer, primary_key=True)
image = db.Column(db.String, nullable=False)
game_id = db.Column(db.Integer, db.ForeignKey("games.id"))
class Tags(db.Model):
id = db.Column(db.Integer, primary_key=True)
tag = db.Column(db.String, nullable=False)
game_id = db.Column(db.Integer, db.ForeignKey("games.id"))
class Authors(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
role = db.Column(db.String, nullable=False, default="Developer")
game_id = db.Column(db.Integer, db.ForeignKey("games.id"))
class Users(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
uuid = db.Column(db.String, nullable=False)
game_id = db.Column(db.Integer, db.ForeignKey("games.id"))
def get_id(self):
return int(self.id)
class Games(db.Model):
id = db.Column(db.Integer, primary_key=True)
approved = db.Column(db.Boolean, nullable=False, default=False)
visible = db.Column(db.Boolean, nullable=False, default=False)
name = db.Column(db.String, nullable=False)
studio = db.Column(db.String, nullable=False)
description = db.Column(db.String, nullable=False)
logo = db.Column(db.String)
background = db.Column(db.String)
downloadLink = db.Column(db.String)
ageRating = db.Column(db.String, nullable=False)
tags = db.relationship("Tags", backref="game", lazy=True)
authors = db.relationship("Authors", backref="game", lazy=True)
images = db.relationship("Images", backref="game", lazy=True)
owner_id = db.relationship("Users", backref="game", lazy=True)

View file

@ -0,0 +1,66 @@
from flask import Blueprint, render_template, redirect, flash, abort
from flask_login import login_user, logout_user, login_required, current_user
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired
from website.models import Users, Games
blueprint = Blueprint("website", __name__)
class LoginForm(FlaskForm):
uuid = StringField(
"uuid",
validators=[DataRequired()],
render_kw={"placeholder": "12345678-ABCD-ABCD-ABCD-123456789EFG"},
)
@blueprint.route("/")
def index():
games = Games.query.filter_by(approved=True).filter_by(visible=True).all()
return render_template("index.html", games=games)
@blueprint.route("/g/<int:game_id>")
def g(game_id):
game = (
Games.query.filter_by(id=game_id)
.filter_by(approved=True)
.filter_by(visible=True)
.first()
)
if not game:
abort(404)
return render_template("game.html", game=game)
@blueprint.route("/editor")
@login_required
def editor():
game = Users.query.filter_by(id=current_user.id).first().game
return render_template("editor.html", game=game)
@blueprint.route("/login", methods=["GET", "POST"])
def login():
form = LoginForm()
if form.validate_on_submit():
if user := Users.query.filter_by(uuid=str(form.uuid.data)).first():
login_user(user, remember=True)
return redirect("/")
else:
flash("Incorrect login")
return render_template("login.html", form=form)
@blueprint.route("/logout")
@login_required
def logout():
logout_user()
return redirect("/")

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View file

@ -0,0 +1,6 @@
function keepRatio() {
let games = document.querySelectorAll(".game-box");
games.forEach((game) => {
game.style.height = (game.offsetWidth * 1.5) + "px";
});
}

View file

@ -0,0 +1,15 @@
window.onscroll = () => {
scrollFunction();
checkSection();
};
window.onload = () => {
keepRatio()
resizeNav();
scrollFunction();
checkSection();
};
window.onresize = () => {
keepRatio()
resizeNav();
checkSection();
};

View file

@ -0,0 +1,82 @@
const defaultTitle = "DV8 Game Expo <span>2023</span>";
let navSpacing = (5 * 16); // The amount of pixels to offset the section by
let prevElement = null;
function resizeNav() {
if (window.innerWidth > 600) {
navSpacing = (5 * 16);
} else {
navSpacing = (8 * 16);
}
}
function scrollFunction() {
let nav = document.querySelector("nav");
// let scrollHeight = window.innerHeight - nav.offsetHeight;
let scrollHeight = 0;
if (document.body.scrollTop > scrollHeight ||
document.documentElement.scrollTop > scrollHeight) {
nav.classList.add("scrolled");
} else {
nav.classList.remove("scrolled");
}
}
function checkSection() {
// Get the nav and sections
let navTitle = document.querySelector(".title > p");
let sections = document.querySelectorAll("section");
// If we're at the top of the page, set the title to the default
if ((window.pageYOffset + navSpacing) < sections[0].offsetTop || window.pageYOffset === 0) {
// If we're already on the default title, don't do anything as it'll break the animation
if (prevElement === null) return;
navTitle.innerHTML = defaultTitle;
navTitle.style.animation = "title-change 0.2s ease-in-out";
prevElement = null;
// Remove the animation after it's done, so we can animate again
setTimeout(() => { navTitle.style.animation = ""; }, 200);
return;
}
// While at this point we may not need to check for the sections
// There aren't many sections, so it's not a big deal
sections.forEach((section) => {
// Get the position of the section
let top = section.offsetTop;
let bottom = section.offsetTop + section.offsetHeight;
// If the section is on the screen is:
// 1. The top of the section is above the top of the screen
// 2. The bottom of the section is below the bottom of the screen
if ((window.pageYOffset + navSpacing) >= top && window.pageYOffset < (bottom - navSpacing)) {
// If we're already on the section, don't do anything as it'll break the animation
if (prevElement === section) return;
navTitle.innerHTML = section.id.split("_").join(" ");
navTitle.style.animation = "title-change 0.2s ease-in-out";
prevElement = section;
// Remove the animation after it's done, so we can animate again
setTimeout(() => { navTitle.style.animation = ""; }, 200);
}
});
}
document.querySelectorAll("nav > ul > li > a").forEach((element) => {
element.onclick = () => {
let anchor = location.hash.split("#")[1].toString();
let element = document.getElementById(anchor);
if (element === null) {
window.scrollTo({ top: 0, behavior: "smooth" });
} else {
window.scrollTo({top: (element.offsetTop + navSpacing), behavior: "smooth"});
}
}
});

View file

@ -0,0 +1,38 @@
@keyframes glow
0%
opacity: 0
50%
opacity: 1
100%
opacity: 0
@keyframes gradient
0%
background-position: 0% 0%
25%
background-position: 100% 50%
50%
background-position: 0% 100%
75%
background-position: 50% 25%
100%
background-position: 0% 50%
@keyframes title-change
0%
transform: translateX(-3rem)
opacity: 0
filter: blur(0.2rem)
100%
transform: translateX(0)
opacity: 1
filter: blur(0)
@media (max-width: 600px)
@keyframes title-change
0%
transform: translateY(-0.5rem)
opacity: 0
100%
transform: translateY(0)
opacity: 1

View file

@ -0,0 +1,36 @@
header
padding: 0 1rem
height: 100vh
max-height: 30rem
display: flex
flex-direction: column
justify-content: center
align-items: center
text-align: center
font-family: $main-font
color: RGB($primary)
> h1
margin: 0
font-size: 3rem
> span
font-family: $monospace-font
font-weight: normal
color: RGB($accent)
> p
margin: 0
font-size: 1.2rem
> img
margin-bottom: 1rem
width: 40rem
height: auto
max-width: 100%
max-height: 30rem
> i
margin: 1rem 0 0
font-size: 1.2rem
animation: glow 3s ease-in-out infinite

View file

@ -0,0 +1,92 @@
nav
padding-left: 1rem
padding-right: 0.5rem
width: 100%
height: 3rem
display: flex
flex-direction: row
align-items: center
position: fixed
top: 0
left: 0
font-weight: bold
font-family: $main-font
font-size: 1.1rem
white-space: nowrap
color: RGB($primary)
// border-bottom: 1px solid RGB($primary)
overflow: hidden
z-index: 100
transition: color 0.1s ease-in-out
&::before
content: ""
position: absolute
inset: 0
background: RGB(var(--nav))
transform: translateY(-3rem)
transition: transform 0.2s ease-in-out
z-index: -1
> span
width: 100%
> ul
margin: 0
padding: 0
height: 3rem
list-style: none
display: flex
flex-direction: row
align-items: center
> li > a
margin: 0 0.75rem
padding: 0.1rem 0.5rem
text-decoration: none
color: inherit
transition: color 0.1s ease-in-out
&:hover
color: RGB($accent)
> .title
height: 3rem
display: flex
flex-direction: row
align-items: center
justify-content: center
> p
margin: auto
font-size: inherit
color: inherit
transition: color 0.1s ease-in-out
> span
font-family: $monospace-font
color: RGB($accent)
&.scrolled
color: RGB($secondary)
&::before
transform: translateY(0)
@media (max-width: 600px)
nav
padding-right: 1rem
height: 6rem
top: -3rem
display: flex
flex-direction: column
justify-content: center
transition: top 0.2s ease-in-out
> .title
opacity: 1
> p
font-size: 1.3rem
&.scrolled
top: 0

View file

@ -0,0 +1,198 @@
section
margin: 3rem auto 0
max-width: 85rem
display: flex
flex-direction: column
gap: 1rem
scroll-margin-top: 4rem
> h2
margin: 0
font-size: 2rem
font-weight: bold
> p
margin: 0
font-size: 1rem
@media (max-width: 600px)
section
scroll-margin-top: 7rem
text-align: center
justify-content: center
.login
padding: 0.5rem
background-color: RGB($primary)
color: RGB($secondary)
border-radius: $radius
> p
margin: 0 0 0.5rem
padding: 0.5rem
background-color: RGB($accent)
color: RGB($primary)
border-radius: calc(calc(#{$radius} - 0.5rem) / 2)
&:first-child
border-top-left-radius: calc(#{$radius} - 0.5rem)
border-top-right-radius: calc(#{$radius} - 0.5rem)
> form
display: flex
flex-direction: row
> input
padding: 0.5rem 1rem
width: 100%
font-size: 1rem
font-family: $monospace-font
background-color: RGB($secondary)
color: RGB($primary)
border-radius: calc(calc(#{$radius} - 0.5rem) / 2) 0 0 calc(#{$radius} - 0.5rem)
border: none
transition: transform 0.1s ease-in-out, border-radius 0.1s ease-in-out
&:hover, &:focus-visible
outline: none
> button
padding: 0.5rem 1rem
font-size: 1rem
background-color: RGB($primary-button)
color: RGB($primary)
border-radius: 0 calc(calc(#{$radius} - 0.5rem) / 2) calc(#{$radius} - 0.5rem) 0
border: none
transition: transform 0.1s ease-in-out, border-radius 0.1s ease-in-out
&:hover, &:focus-visible
outline: none
background-color: RGB($secondary-button)
.games
margin-top: 1rem
display: flex
flex-direction: row
flex-wrap: wrap
gap: 1rem
.game-box
margin: 0 auto
padding: 1rem
width: 16rem
height: 0
position: relative
display: flex
flex-direction: column
font-family: $main-font
font-size: 1rem
text-decoration: none
text-align: center
background-color: RGB($primary)
color: RGB($secondary)
border-radius: $radius
box-shadow: 0 0.2rem 1rem 0 RGB($primary)
transition: box-shadow 0.1s ease-in-out, transform 0.25s ease-in-out
overflow: hidden
.background
position: absolute
inset: 0
width: 100%
height: 100%
object-fit: cover
opacity: 0.3
filter: blur(0.25rem)
z-index: +1
&::after
content: ''
position: absolute
inset: 0
background-image: linear-gradient(to top, transparent, RGB($primary))
z-index: +2
> div
position: relative
height: 100%
display: flex
flex-direction: column
gap: 0.5rem
font-weight: bold
z-index: +3
.logo
margin: 0 auto 1rem
width: auto
height: auto
max-width: 100%
max-height: 40%
display: block
border-radius: calc(#{$radius} / 2)
> h2
margin: 0
font-size: 2rem
color: RGB($accent)
> p
margin: 0
> span
height: 100%
> ul
margin: 0
padding: 0
list-style: none
display: flex
flex-direction: row
flex-wrap: wrap
gap: 0.5rem
> li
margin: 0
padding: 0.25rem 0.5rem
font-size: 0.9rem
font-weight: normal
background-color: RGBA($accent, 0.5)
color: RGB($secondary)
border-radius: $radius
&:hover
box-shadow: 0 0.25rem 1.25rem 0 RGB($primary)
transform: scale(1.03) translateY(-0.25rem)
@media (max-width: 600px)
.game-box
margin-bottom: 2rem
padding: 0.75rem
width: 14rem
> div
.logo
margin-bottom: 0.5rem
max-height: 25%
> h2
font-size: 1.5rem

View file

@ -0,0 +1,124 @@
$primary: var(--primary)
$secondary: var(--secondary)
$primary-button: var(--primary-button)
$secondary-button: var(--secondary-button)
$accent: var(--accent)
$radius: var(--radius)
$main-font: var(--main-font)
$monospace-font: var(--monospace-font)
// Accessibility setting
//*, *::before, *::after
// animation-duration: 0s !important
// transition: none !important
//--dv8-orange: #F36023
//--dv8-yellow: #FFE11C
//--dv8-cyan: #00AAB0
//--dv8-magenta: #D51E90
//--dv8-lime: #C0D939
\:root
--primary: 43, 43, 43
--secondary: 240, 240, 245
--primary-button: 242, 96, 34
--secondary-button: 191, 85, 40
--accent: 194, 165, 136
--radius: 0.3rem
--main-font: 'Rubik', sans-serif
--monospace-font: 'JetBrains Mono', monospace
--nav: 35, 35, 35
@import "animations"
@import "nav"
@import "header"
@import "sections"
*
box-sizing: border-box
html
font-family: $main-font
background-color: RGB($secondary)
color: RGB($primary)
body
margin: 0
padding: 0
min-height: 100vh
display: grid
grid-template-rows: 1fr auto
.background
background-color: RGB($secondary)
position: absolute
inset: 0
overflow: hidden
z-index: 1
> img
position: absolute
inset: -5%
width: 110%
height: 110%
object-fit: cover
filter: blur(0.25rem)
opacity: 0.6
&::after
content: ''
position: absolute
inset: 0
background-image: linear-gradient(to top, RGB($secondary) 3%, RGBA($primary, 0.1))
z-index: +1
main
padding: 3rem 2rem 2rem
position: relative
z-index: 2
@media (max-width: 600px)
main
padding: 3rem 1rem 1rem
footer
margin: auto 0 0
padding: 0.5rem
position: relative
display: flex
justify-content: center
align-items: center
background-color: RGB(var(--nav))
color: RGB($secondary)
z-index: 2
> p
margin: 0
font-size: 0.8rem
font-family: $monospace-font
text-align: center
color: RGB($secondary)
> a
margin: 0
font-size: inherit
font-family: inherit
color: RGB($accent)
text-decoration: none
cursor: pointer
&:hover
text-decoration: underline

View file

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Game Event 23</title>
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap">
<!-- Phosphor Icons -->
<script src="https://unpkg.com/@phosphor-icons/web"></script>
<!-- Stylesheets -->
{% assets "scripts" %}<script src="{{ ASSET_URL }}" defer></script>{% endassets %}
<!-- Scripts -->
{% assets "styles" %}<link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css" defer>{% endassets %}
</head>
<body>
<nav>
<div class="title"><p>DV8 Game Expo <span>2023</span></p></div>
<span><!-- This is a separator --></span>
<ul>
<li><a href="{{ url_for('website.index') }}#">Home</a></li>
<li><a href="{{ url_for('website.index') }}#What_is_this?">About</a></li>
<li><a href="{{ url_for('website.index') }}#Student_Games">Games</a></li>
{% if current_user.is_authenticated %}<li><a href="{{ url_for('website.logout') }}">Logout</a></li>{% endif %}
</ul>
</nav>
<span class="background">
<img src="{% block background %}{% endblock %}" alt="Background">
</span>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>Game Event 2023 | <a href="https://github.com/Fluffy-Bean/GameExpo23" target="_blank">Server Source</a></p>
</footer>
</body>
</html>

View file

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block background %}{{ url_for('static', filename='images/default.jpg') }}{% endblock %}
{% block content %}
<header>
<h1>{{ game.name }}</h1>
<p>Editor</p>
</header>
{% endblock %}

View file

@ -0,0 +1,43 @@
{% extends "base.html" %}
{% block background %}
{% if game.background %}
{{ url_for('static', filename='images/backgrounds/' + game.background) }}
{% else %}
{{ url_for('static', filename='images/default.jpg') }}
{% endif %}
{% endblock %}
{% block content %}
<header>
{% if game.logo %}
<img src="{{ url_for('static', filename='images/logos/' + game.logo) }}" alt="{{ game.name }} Logo">
{% else %}
<h1>{{ game.name }}</h1>
{% endif %}
<p>By {{ game.studio }}</p>
<p>
{% for person in game.authors %}
{{ person.name }}
{% if not loop.last %}, {% endif %}
{% endfor %}
</p>
</header>
<section id="Description">
<h2>Description</h2>
<p>{{ game.description }}</p>
</section>
<section id="Tags">
<ul>
{% for tag in game.tags %}
<li>{{ tag }}</li>
{% endfor %}
</ul>
</section>
<section id="Images">
{% for image in game.images %}
<img src="{{ url_for('static', filename='images/user/' + image) }}" alt="{{ image.alt }}">
{% endfor %}
</section>
{% endblock %}

View file

@ -0,0 +1,48 @@
{% extends "base.html" %}
{% block background %}{{ url_for('static', filename='images/default.jpg') }}{% endblock %}
{% block content %}
<header>
<p>Welcome to the</p>
<h1>DV8 Game Expo <span data-text="2023">2023</span>!</h1>
<i class="ph-bold ph-caret-double-down"></i>
</header>
<section id="What_is_this?">
<h2>What is this?</h2>
<p>The DV8 Game Expo, is a showcase of the works and efforts of students from the past year. This includes Level 3 year 1 and year 2.</p>
</section>
<section id="Student_Games">
<h2>Student Games</h2>
<p>Here are some games AAAA</p>
<div class="games">
{% for game in games %}
<a class="game-box" href="{{ url_for('website.g', game_id=game.id) }}">
{% if game.background %}
<img src="{{ url_for('static', filename='images/backgrounds/' + game.background) }}" alt="{{ game.name }}" class="background">
{% else %}
<img src="{{ url_for('static', filename='images/default.jpg') }}" alt="{{ game.name }}" class="background">
{% endif %}
<div>
{% if game.logo %}
<img src="{{ url_for('static', filename='images/logos/' + game.logo) }}" alt="{{ game.name }}" class="logo">
{% else %}
<h2>{{ game.name }}</h2>
{% endif %}
<p>{{ game.studio }}</p>
<!-- <p>{{ game.description|truncate(100) }}</p> -->
<span><!-- Seperator --></span>
<ul>
{% for tag in game.tags %}
<li>{{ tag.tag }}</li>
{% endfor %}
</ul>
</div>
</a>
{% endfor %}
</div>
</section>
{% endblock %}

View file

@ -0,0 +1,22 @@
{% extends "base.html" %}
{% block background %}{{ url_for('static', filename='images/default.jpg') }}{% endblock %}
{% block content %}
<section class="center">
<div class="login">
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
{% else %}
<p>Do not share your UUID</p>
{% endif %}
{% endwith %}
<form action="{{ url_for('website.login') }}" method="POST">
{{ form.csrf_token }}
{{ form.uuid(size=36) }}
<button type="submit">Login</button>
</form>
</div>
</section>
{% endblock %}