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/
|
||||
|
||||
# ---> 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.log import Log
|
||||
from starfall.web import App
|
||||
|
||||
if __name__ == "__main__":
|
||||
Log.setup()
|
||||
|
||||
try:
|
||||
Config.load()
|
||||
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 ==
|
||||
|
||||
[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.
|
||||
port = 5000
|
||||
# Connection database url.
|
||||
# sqlite is only production safe to a point - consult the SQLAlchemy
|
||||
# documentation to see how to set up other databases.
|
||||
database_url = "sqlite:///starfallbot.db"
|
||||
# Secret key, used to sign the session cookie.
|
||||
secret_key = "PRIVATE SECRET KEY CHANGEME"
|
||||
|
||||
[discord]
|
||||
token = "YOUR BOT TOKEN HERE"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
discord-py-interactions
|
||||
flask
|
||||
flask-sqlalchemy
|
||||
discord-py-interactions
|
||||
rich
|
||||
twitchio
|
||||
|
|
@ -1,14 +1,24 @@
|
|||
import os
|
||||
from shutil import copy
|
||||
from typing import Any
|
||||
|
||||
import tomllib
|
||||
from typing import Annotated, Optional
|
||||
|
||||
|
||||
class Config:
|
||||
data: Annotated[Optional[dict],
|
||||
"Configuration imported from config.toml"] = None
|
||||
data: dict[str, Any] = dict()
|
||||
"""Configuration imported from config.toml."""
|
||||
|
||||
@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.
|
||||
|
||||
Raises:
|
||||
|
|
@ -16,14 +26,23 @@ class Config:
|
|||
"""
|
||||
if not cls.file_exists():
|
||||
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)
|
||||
|
||||
@classmethod
|
||||
def file_exists(cls) -> bool:
|
||||
"""Whether the config.toml file exists or needs to be created.
|
||||
|
||||
|
||||
Returns:
|
||||
bool: True if path exists and is a file, False otherwise.
|
||||
"""
|
||||
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