Create phase 1 - web service - db is dummy for now, noop
This commit is contained in:
parent
b2efaf340e
commit
77b43b3030
|
|
@ -175,4 +175,5 @@ cython_debug/
|
||||||
#.idea/
|
#.idea/
|
||||||
|
|
||||||
# ---> StarfallBot
|
# ---> StarfallBot
|
||||||
config.toml
|
config.toml
|
||||||
|
web/static/style/*.scss
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"name": "Python: Current File",
|
||||||
|
"program": "${workspaceFolder}/app.py",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "debugpy"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "0.2.0"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.ruff": "always",
|
||||||
|
"source.organizeImports.ruff": "always",
|
||||||
|
"source.sort.json": "always"
|
||||||
|
},
|
||||||
|
"python.languageServer": "None",
|
||||||
|
"scss-to-css-compile.browsers": [
|
||||||
|
"ie > 9",
|
||||||
|
"iOS > 8",
|
||||||
|
"Android >= 4.4",
|
||||||
|
"ff > 38",
|
||||||
|
"Chrome > 38"
|
||||||
|
],
|
||||||
|
"scss-to-css-compile.compileOnSave": false,
|
||||||
|
"scss-to-css-compile.outDir": "./",
|
||||||
|
"scss-to-css-compile.output": "compressed"
|
||||||
|
}
|
||||||
27
app.py
27
app.py
|
|
@ -1,7 +1,32 @@
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
from starfall.config import Config
|
from starfall.config import Config
|
||||||
|
from starfall.log import Log
|
||||||
|
from starfall.web import App
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Log.setup()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Config.load()
|
Config.load()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
dst = Config.create()
|
||||||
|
logging.getLogger().critical(
|
||||||
|
msg="\n".join(
|
||||||
|
[
|
||||||
|
"*** Config file was not found ***",
|
||||||
|
"",
|
||||||
|
"Sample config.toml file has been created in the project's root directory:",
|
||||||
|
"[b]" + os.path.realpath("./%s" % dst) + "[/b]",
|
||||||
|
"Edit it and run the program again.",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
extra={"markup": True},
|
||||||
|
)
|
||||||
|
exit()
|
||||||
|
|
||||||
|
App.__init__()
|
||||||
|
|
||||||
|
threads = [Thread(App.run())]
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
# == StarfallBot configuration file ==
|
# == StarfallBot configuration file ==
|
||||||
|
|
||||||
[web]
|
[web]
|
||||||
|
# What IP the web server is bound to.
|
||||||
|
# Use "localhost" until the bot is production ready.
|
||||||
|
host = "localhost"
|
||||||
# What port the web server uses.
|
# What port the web server uses.
|
||||||
port = 5000
|
port = 5000
|
||||||
# Connection database url.
|
# Connection database url.
|
||||||
# sqlite is only production safe to a point - consult the SQLAlchemy
|
# sqlite is only production safe to a point - consult the SQLAlchemy
|
||||||
# documentation to see how to set up other databases.
|
# documentation to see how to set up other databases.
|
||||||
database_url = "sqlite:///starfallbot.db"
|
database_url = "sqlite:///starfallbot.db"
|
||||||
|
# Secret key, used to sign the session cookie.
|
||||||
|
secret_key = "PRIVATE SECRET KEY CHANGEME"
|
||||||
|
|
||||||
[discord]
|
[discord]
|
||||||
token = "YOUR BOT TOKEN HERE"
|
token = "YOUR BOT TOKEN HERE"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
|
discord-py-interactions
|
||||||
flask
|
flask
|
||||||
flask-sqlalchemy
|
flask-sqlalchemy
|
||||||
discord-py-interactions
|
rich
|
||||||
twitchio
|
twitchio
|
||||||
|
|
@ -1,14 +1,24 @@
|
||||||
import os
|
import os
|
||||||
|
from shutil import copy
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import tomllib
|
import tomllib
|
||||||
from typing import Annotated, Optional
|
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
data: Annotated[Optional[dict],
|
data: dict[str, Any] = dict()
|
||||||
"Configuration imported from config.toml"] = None
|
"""Configuration imported from config.toml."""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls):
|
def create(cls) -> str:
|
||||||
|
"""Copy config-sample.toml file to config.toml.
|
||||||
|
|
||||||
|
Does not alert the user to the modification.
|
||||||
|
"""
|
||||||
|
return copy(src="config-sample.toml", dst="config.toml")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls) -> None:
|
||||||
"""Loads the contents of the config.toml file into the class.
|
"""Loads the contents of the config.toml file into the class.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
|
|
@ -16,14 +26,23 @@ class Config:
|
||||||
"""
|
"""
|
||||||
if not cls.file_exists():
|
if not cls.file_exists():
|
||||||
raise FileNotFoundError("config.toml does not exist")
|
raise FileNotFoundError("config.toml does not exist")
|
||||||
with open("config.toml", "r") as f:
|
with open("config.toml", "rb") as f:
|
||||||
cls.data = tomllib.load(f)
|
cls.data = tomllib.load(f)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def file_exists(cls) -> bool:
|
def file_exists(cls) -> bool:
|
||||||
"""Whether the config.toml file exists or needs to be created.
|
"""Whether the config.toml file exists or needs to be created.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if path exists and is a file, False otherwise.
|
bool: True if path exists and is a file, False otherwise.
|
||||||
"""
|
"""
|
||||||
return os.path.exists("config.toml") and os.path.isfile("config.toml")
|
return os.path.exists("config.toml") and os.path.isfile("config.toml")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, key: str, default: Any):
|
||||||
|
"""Get config key, if it exists, or return a default.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Any: Result, or default if key does not exist.
|
||||||
|
"""
|
||||||
|
return cls.data.get(key, default)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
|
db = SQLAlchemy()
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import werkzeug
|
||||||
|
from rich.logging import RichHandler
|
||||||
|
|
||||||
|
|
||||||
|
class Log:
|
||||||
|
@classmethod
|
||||||
|
def setup(cls) -> None:
|
||||||
|
handler = RichHandler(level=logging.DEBUG, rich_tracebacks=True)
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format="%(message)s",
|
||||||
|
datefmt="[%Y-%m-%d %H:%M:%S]",
|
||||||
|
handlers=[handler],
|
||||||
|
)
|
||||||
|
|
||||||
|
# NOTE: pyright's reportPrivateUsage is suppressed here.
|
||||||
|
# We disable ANSI terminal codes from being added to Werkzeug (Flask) logs;
|
||||||
|
# RichHandler already handles all the fancy color stuff.
|
||||||
|
werkzeug.serving._log_add_style = False # pyright: ignore[reportPrivateUsage]
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
import os
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
from starfall.config import Config
|
||||||
|
from starfall.db import db
|
||||||
|
from starfall.web.home import home_blueprint
|
||||||
|
|
||||||
|
|
||||||
|
class App:
|
||||||
|
app: Flask = Flask(__name__)
|
||||||
|
config: dict[str, Any] = dict() # pyright: ignore[reportExplicitAny]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __init__(cls):
|
||||||
|
cls.load_config()
|
||||||
|
cls.app.config.update( # pyright: ignore[reportUnknownMemberType]
|
||||||
|
SECRET_KEY=cls.config.get("secret_key"),
|
||||||
|
SQLALCHEMY_DATABASE_URI=cls.config.get("database_url"),
|
||||||
|
)
|
||||||
|
|
||||||
|
cls.app.root_path = os.path.realpath(".")
|
||||||
|
cls.app.static_folder = os.path.realpath("./web/static")
|
||||||
|
cls.app.template_folder = "web"
|
||||||
|
|
||||||
|
db.init_app(cls.app)
|
||||||
|
cls.app.register_blueprint(home_blueprint)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_config(cls):
|
||||||
|
cls.config = Config.get("web", {})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls):
|
||||||
|
cls.app.run(host=cls.config.get("host"), port=cls.config.get("port"))
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
from flask import Blueprint, render_template
|
||||||
|
|
||||||
|
home_blueprint: Blueprint = Blueprint("main", __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@home_blueprint.route("/")
|
||||||
|
def index():
|
||||||
|
return render_template("home.html")
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
{% extends "index.html" %}
|
||||||
|
|
||||||
|
{% block titleSuffix %}Overview{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<div class="flex center">
|
||||||
|
<h1>StarfallBot - Overview</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>StarfallBot - {% block titleSuffix %}Unassigned{% endblock %}</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||||
|
<meta name="description" content="StarfallBot administation interface" />
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style/fonts.css') }}" />
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style/main.css') }}" />
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.png') }}" />
|
||||||
|
{% block head %}
|
||||||
|
{% endblock %}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% block beforeMain %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<main>
|
||||||
|
{% block beforeMainContainer %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<p>No content has been defined for this page.</p>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% block afterMainContainer %}
|
||||||
|
{% endblock %}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{% block afterMain %}
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,35 @@
|
||||||
|
/* exo-2-regular - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: 'Exo 2';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('../fonts/exo-2-v26-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exo-2-700 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: 'Exo 2';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url('../fonts/exo-2-v26-latin-700.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overpass-regular - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: 'Overpass';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('../fonts/overpass-v19-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overpass-700 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: 'Overpass';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url('../fonts/overpass-v19-latin-700.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
body{position:relative;min-height:100%;color:#000;background-image:linear-gradient(to bottom, #205080, #4070a0);background-repeat:no-repeat;background-attachment:fixed;font-family:"Overpass",sans-serif;font-size:18px}main{height:100%;display:flex;justify-content:center}main>.container{width:100%;height:100%;max-width:1400px;padding:8px;background:#001030;border-radius:16px;color:#fff}main .flex{display:flex;flex-flow:row wrap}main .flex.center{justify-content:center}h1{font-family:"Exo 2",sans-serif;font-size:4rem;font-weight:bold;padding:0;margin:0}h2{font-family:"Exo 2",sans-serif;font-size:2rem;font-weight:bold;padding:0;margin:0}h3{font-family:"Exo 2",sans-serif;font-size:1.5rem;font-weight:bold;padding:0;margin:0}h4{font-family:"Exo 2",sans-serif;font-size:1rem;font-weight:bold;padding:0;margin:0}h5{font-family:"Exo 2",sans-serif;font-size:.875rem;font-weight:bold;padding:0;margin:0}h6{font-family:"Exo 2",sans-serif;font-size:.75rem;font-weight:bold;padding:0;margin:0}
|
||||||
Loading…
Reference in New Issue