mirror of
https://github.com/Fluffy-Bean/GameExpo23.git
synced 2025-05-18 09:24:52 +00:00
Yeet expo page
Fix score uploading
This commit is contained in:
parent
85a329ad6d
commit
a93a8cf04d
40 changed files with 109 additions and 1247 deletions
|
@ -1,9 +1,4 @@
|
|||
{$THE_FRONT_ROOMS_DOMAIN} {
|
||||
{$THE_FRONT_ROOMS_DOMAIN}
|
||||
reverse_proxy tfr:8000
|
||||
encode gzip
|
||||
}
|
||||
|
||||
{$GAME_EXPO_DOMAIN} {
|
||||
reverse_proxy expo:5000
|
||||
encode gzip
|
||||
}
|
||||
|
|
168
GameExpo/.gitignore
vendored
168
GameExpo/.gitignore
vendored
|
@ -1,168 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
# 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"]
|
|
@ -1,24 +0,0 @@
|
|||
# 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
|
||||
```
|
|
@ -1,12 +0,0 @@
|
|||
Gunicorn
|
||||
Flask
|
||||
Flask-SQLAlchemy
|
||||
Flask-Migrate
|
||||
Flask-Login
|
||||
WTForms
|
||||
Flask-WTF
|
||||
Flask-Assets
|
||||
Flask-Caching
|
||||
libsass-bin
|
||||
jsmin
|
||||
cssmin
|
|
@ -1,20 +0,0 @@
|
|||
#!/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
|
|
@ -1,35 +0,0 @@
|
|||
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()
|
|
@ -1,15 +0,0 @@
|
|||
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"
|
|
@ -1,11 +0,0 @@
|
|||
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()
|
|
@ -1,54 +0,0 @@
|
|||
"""
|
||||
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)
|
|
@ -1,66 +0,0 @@
|
|||
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.
Before Width: | Height: | Size: 2.1 MiB |
Binary file not shown.
Before Width: | Height: | Size: 57 KiB |
Binary file not shown.
Before Width: | Height: | Size: 152 KiB |
Binary file not shown.
Before Width: | Height: | Size: 57 KiB |
|
@ -1,6 +0,0 @@
|
|||
function keepRatio() {
|
||||
let games = document.querySelectorAll(".game-box");
|
||||
games.forEach((game) => {
|
||||
game.style.height = (game.offsetWidth * 1.5) + "px";
|
||||
});
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
window.onscroll = () => {
|
||||
scrollFunction();
|
||||
checkSection();
|
||||
};
|
||||
window.onload = () => {
|
||||
keepRatio()
|
||||
resizeNav();
|
||||
scrollFunction();
|
||||
checkSection();
|
||||
};
|
||||
window.onresize = () => {
|
||||
keepRatio()
|
||||
resizeNav();
|
||||
checkSection();
|
||||
};
|
|
@ -1,82 +0,0 @@
|
|||
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"});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,38 +0,0 @@
|
|||
@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
|
|
@ -1,36 +0,0 @@
|
|||
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
|
|
@ -1,92 +0,0 @@
|
|||
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
|
|
@ -1,198 +0,0 @@
|
|||
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
|
|
@ -1,124 +0,0 @@
|
|||
$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
|
|
@ -1,47 +0,0 @@
|
|||
<!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>
|
|
@ -1,8 +0,0 @@
|
|||
{% 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 %}
|
|
@ -1,43 +0,0 @@
|
|||
{% 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 %}
|
|
@ -1,48 +0,0 @@
|
|||
{% 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 %}
|
|
@ -1,22 +0,0 @@
|
|||
{% 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 %}
|
14
README.md
14
README.md
|
@ -1,6 +1,12 @@
|
|||
# GameExpo23
|
||||
The all-new and improved DV8 Game Expo website
|
||||
All new and improved DV8 Game Expo project
|
||||
While the Expo page got scrapped due to multiple reasons, this repo still includes the following:
|
||||
|
||||
- TheFrontRooms Server
|
||||
- Postgres Database for the Server
|
||||
- Caddy Proxy for hosting
|
||||
|
||||
If, like me, you want to run the Postgres server online for whatever reason, I suggest giving your database a very stong password, change the default user and database names and use the following guide for `fail2ban` support
|
||||
|
||||
https://warlord0blog.wordpress.com/2022/09/14/fail2ban-postgresql/
|
||||
|
||||
# Nerd Info
|
||||
Written on the Flask app-factory style with Python
|
||||
Ran with Gunicorn on Docker, using Caddy for the reverse-proxy
|
||||
|
|
2
TFR/.gitignore
vendored
2
TFR/.gitignore
vendored
|
@ -120,7 +120,7 @@ celerybeat.pid
|
|||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
../.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
|
|
|
@ -27,3 +27,4 @@ fi
|
|||
# Start server!!!!
|
||||
echo "Starting server..."
|
||||
gunicorn --bind tfr:8000 server:app
|
||||
# flask --app server run --port 8000 --host tfr
|
||||
|
|
|
@ -7,7 +7,7 @@ from werkzeug.exceptions import HTTPException
|
|||
from server.extensions import db, migrate, cache, assets, login_manager
|
||||
from server.models import Users
|
||||
from server.config import MIGRATION_DIR, INSTANCE_DIR
|
||||
from server import views, auth, api
|
||||
from server import views, auth, api, filters
|
||||
|
||||
|
||||
app = Flask(__name__, instance_path=INSTANCE_DIR)
|
||||
|
@ -39,6 +39,7 @@ cache.init_app(app)
|
|||
app.register_blueprint(views.blueprint)
|
||||
app.register_blueprint(auth.blueprint)
|
||||
app.register_blueprint(api.blueprint)
|
||||
app.register_blueprint(filters.blueprint)
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
|
|
|
@ -43,39 +43,34 @@ def tokens():
|
|||
|
||||
@blueprint.route("/post", methods=["POST"])
|
||||
def post():
|
||||
form = request.form
|
||||
error = []
|
||||
session_key = request.form.get("session", None)
|
||||
version = request.form.get("version", "alpha")
|
||||
difficulty = request.form.get("difficulty", 0)
|
||||
score = request.form.get("score", 0)
|
||||
|
||||
if not form:
|
||||
error.append("No form data provided!")
|
||||
if not form["session"]:
|
||||
error.append("No session key provided!")
|
||||
if not form["version"]:
|
||||
error.append("No version provided!")
|
||||
|
||||
if error:
|
||||
return jsonify(error), 400
|
||||
if not session_key:
|
||||
return "No session key provided!"
|
||||
if not score:
|
||||
return "Score is not valid!"
|
||||
|
||||
try:
|
||||
int(form["score"])
|
||||
int(form["difficulty"])
|
||||
float(score)
|
||||
int(difficulty)
|
||||
except TypeError:
|
||||
error.append("Invalid score and difficulty must be valid numbers!")
|
||||
return "Invalid score and difficulty must be valid numbers!"
|
||||
|
||||
if int(form["difficulty"]) not in GAME_DIFFICULTIES:
|
||||
error.append("Invalid difficulty!")
|
||||
if int(difficulty) not in GAME_DIFFICULTIES:
|
||||
return "Invalid difficulty!"
|
||||
|
||||
session_data = Sessions.query.filter_by(auth_key=form["session"]).first()
|
||||
|
||||
session_data = Sessions.query.filter_by(auth_key=session_key).first()
|
||||
if not session_data:
|
||||
error.append("Authentication failed!")
|
||||
|
||||
if error:
|
||||
return jsonify(error), 400
|
||||
return "Authentication failed!"
|
||||
|
||||
score = Scores(
|
||||
score=int(form["score"]),
|
||||
difficulty=int(form["difficulty"]),
|
||||
version=form["version"],
|
||||
score=float(score),
|
||||
difficulty=int(difficulty),
|
||||
version=version,
|
||||
user_id=session_data.user_id,
|
||||
)
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ def login():
|
|||
if error:
|
||||
for err in error:
|
||||
flash(err, "error")
|
||||
return redirect(url_for("auth.account"))
|
||||
return redirect(url_for("views.account"))
|
||||
|
||||
login_user(user, remember=True)
|
||||
flash("Successfully logged in!", "success")
|
||||
|
|
13
TFR/server/filters.py
Normal file
13
TFR/server/filters.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
import datetime
|
||||
from flask import Blueprint
|
||||
|
||||
|
||||
blueprint = Blueprint('filters', __name__, template_folder='templates')
|
||||
|
||||
|
||||
@blueprint.app_template_filter()
|
||||
def format_result(dttm):
|
||||
dttm = str(dttm).split('.')
|
||||
time = datetime.timedelta(seconds=int(dttm[0]))
|
||||
microtime = dttm[1][:3]
|
||||
return f'{time}:{microtime}'
|
|
@ -16,9 +16,11 @@ class Scores(db.Model):
|
|||
"""
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey("users.id", use_alter=True))
|
||||
|
||||
score = db.Column(db.Float, nullable=False)
|
||||
difficulty = db.Column(db.Integer, nullable=False)
|
||||
|
||||
scored_at = db.Column(
|
||||
db.DateTime,
|
||||
nullable=False,
|
||||
|
@ -31,7 +33,6 @@ class Scores(db.Model):
|
|||
)
|
||||
|
||||
version = db.Column(db.String, default=GAME_VERSION)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey("users.id", use_alter=True))
|
||||
|
||||
|
||||
class Sessions(db.Model):
|
||||
|
@ -41,9 +42,11 @@ class Sessions(db.Model):
|
|||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey("users.id", use_alter=True))
|
||||
|
||||
auth_key = db.Column(db.String, nullable=False, unique=True)
|
||||
ip_address = db.Column(db.String)
|
||||
device_type = db.Column(db.String)
|
||||
|
||||
created_at = db.Column(
|
||||
db.DateTime,
|
||||
nullable=False,
|
||||
|
@ -56,14 +59,14 @@ class Sessions(db.Model):
|
|||
)
|
||||
|
||||
|
||||
class Reset(db.Model):
|
||||
class PasswordReset(db.Model):
|
||||
"""
|
||||
Password reset table
|
||||
"""
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
user_id = db.Column(db.Integer, db.ForeignKey("users.id", use_alter=True))
|
||||
|
||||
reset_key = db.Column(db.String, nullable=False, unique=True)
|
||||
|
||||
created_at = db.Column(
|
||||
|
@ -73,6 +76,36 @@ class Reset(db.Model):
|
|||
)
|
||||
|
||||
|
||||
class Permissions(db.Model):
|
||||
"""
|
||||
Permissions table
|
||||
"""
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey("users.id", use_alter=True))
|
||||
|
||||
user_ban = db.Column(db.Boolean, default=False)
|
||||
user_warn = db.Column(db.Boolean, default=False)
|
||||
|
||||
score_removal = db.Column(db.Boolean, default=False)
|
||||
score_edit = db.Column(db.Boolean, default=False)
|
||||
|
||||
admin_panel = db.Column(db.Boolean, default=False)
|
||||
admin_promote = db.Column(db.Boolean, default=False)
|
||||
admin_demote = db.Column(db.Boolean, default=False)
|
||||
|
||||
|
||||
class ProfileTags(db.Model):
|
||||
"""
|
||||
Profile Tags table
|
||||
"""
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey("users.id", use_alter=True))
|
||||
|
||||
tag = db.Column(db.String, nullable=False)
|
||||
|
||||
|
||||
class Users(db.Model, UserMixin):
|
||||
"""
|
||||
User table
|
||||
|
@ -93,6 +126,8 @@ class Users(db.Model, UserMixin):
|
|||
|
||||
scores = db.relationship("Scores", backref=db.backref("users", lazy=True))
|
||||
tokens = db.relationship("Sessions", backref=db.backref("users", lazy=True))
|
||||
reset = db.relationship("PasswordReset", backref=db.backref("users", lazy=True))
|
||||
tags = db.relationship("ProfileTags", backref=db.backref("users", lazy=True))
|
||||
|
||||
def get_id(self):
|
||||
return str(self.alt_id)
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h2>What is The Front Rooms?</h2>
|
||||
<h2>Project Redacted</h2>
|
||||
<p>The Front Rooms is a game based on The Backrooms Genre of games.</p>
|
||||
|
||||
<h2>Is my data secured?</h2>
|
||||
<p>Yes, all passwords and emails are hashed and salted, and at no point stored in plain text.</p>
|
||||
{% endblock %}
|
|
@ -6,12 +6,12 @@
|
|||
<form action="{{ url_for('auth.login') }}" method="POST">
|
||||
<span class="text-input">
|
||||
<label for="login-username">Username</label>
|
||||
<input type="text" name="username" placeholder="Jerry" id="login-username" required>
|
||||
<input type="text" name="username" id="login-username" required>
|
||||
</span>
|
||||
|
||||
<span class="text-input">
|
||||
<label for="login-password">Password</label>
|
||||
<input type="password" name="password" placeholder="password123" id="login-password" required>
|
||||
<input type="password" name="password" id="login-password" required>
|
||||
</span>
|
||||
|
||||
<button type="submit" class="button primary">Login</button>
|
||||
|
@ -24,17 +24,17 @@
|
|||
<form action="{{ url_for('auth.register') }}" method="POST">
|
||||
<span class="text-input">
|
||||
<label for="register-username">Username</label>
|
||||
<input type="text" name="username" placeholder="Jerry" id="register-username" required>
|
||||
<input type="text" name="username" id="register-username" required>
|
||||
</span>
|
||||
|
||||
<span class="text-input">
|
||||
<label for="register-email">Username</label>
|
||||
<input type="text" name="email" placeholder="jerry@example.com" id="register-email" required>
|
||||
<label for="register-email">Email</label>
|
||||
<input type="text" name="email" id="register-email" required>
|
||||
</span>
|
||||
|
||||
<span class="text-input">
|
||||
<label for="register-password">Password</label>
|
||||
<input type="password" name="password" placeholder="password123" id="register-password" required>
|
||||
<input type="password" name="password" id="register-password" required>
|
||||
</span>
|
||||
|
||||
<button type="submit" class="button primary">Register</button>
|
||||
|
|
|
@ -12,10 +12,12 @@
|
|||
</select>
|
||||
|
||||
<select name="ver" class="button">
|
||||
<option value="alpha" {% if ver=="alpha" %}selected{% endif %}>Alpha</option>
|
||||
<option value="beta" {% if ver=="beta" %}selected{% endif %}>Beta</option>
|
||||
<option value="1.0" {% if ver=="1.0" %}selected{% endif %}>1.0</option>
|
||||
<option value="1.1" {% if ver=="1.1" %}selected{% endif %}>1.1</option>
|
||||
{% for game_version in config["GAME_VERSIONS"] %}
|
||||
<option
|
||||
value="{{ game_version }}"
|
||||
{% if ver==game_version %}selected{% endif %}
|
||||
>{{ game_version }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<span class="text-input">
|
||||
|
@ -56,7 +58,7 @@
|
|||
<td>{{ score.users.username }}</td>
|
||||
{% endif %}
|
||||
|
||||
<td>{{ score.score }}</td>
|
||||
<td>{{ score.score | format_result }}</td>
|
||||
<td>{{ score.scored_at.strftime('%Y-%m-%d') }}</td>
|
||||
<!-- <td>{{ score.version }}</td> -->
|
||||
</tr>
|
||||
|
|
|
@ -13,22 +13,22 @@ services:
|
|||
- ./Caddy/config:/config
|
||||
environment:
|
||||
THE_FRONT_ROOMS_DOMAIN: ${THE_FRONT_ROOMS_DOMAIN}
|
||||
GAME_EXPO_DOMAIN: ${GAME_EXPO_DOMAIN}
|
||||
links:
|
||||
- tfr
|
||||
# - expo
|
||||
|
||||
db:
|
||||
image: postgres:alpine
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
# - "5432:5432"
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- ./Postgres/data:/var/lib/postgresql/data
|
||||
- /var/log/postgresql:/var/log/postgresql
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
command: postgres -c log_connections=on -c log_line_prefix='%m {%h} [%p] %q%u@%d ' -c log_directory='/var/log/postgresql' -c log_truncate_on_rotation=off -c log_rotation_age=1d
|
||||
links:
|
||||
- tfr
|
||||
|
||||
|
@ -44,12 +44,3 @@ services:
|
|||
DB_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
DB_HOST: db
|
||||
DB_NAME: ${POSTGRES_DB}
|
||||
|
||||
# expo:
|
||||
# build: GameExpo
|
||||
# restart: unless-stopped
|
||||
# volumes:
|
||||
# - ./GameExpo/storage:/data/storage
|
||||
# - ./GameExpo/logs:/data/logs
|
||||
# environment:
|
||||
# FLASK_KEY: ${GAME_EXPO_SECRETE_KEY}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue