diff --git a/app/__init__.py b/app/__init__.py index bd0f573..3be187d 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -4,7 +4,12 @@ import os def create_app(): app = Flask(__name__) - app.config["DB_PATH"] = "data/db/db.sqlite" + if os.getenv("PYROM_PROD") is None: + app.static_folder = os.path.join(os.path.dirname(__file__), "../data/static") + app.debug = True + app.config["DB_PATH"] = "data/db/db.dev.sqlite" + else: + app.config["DB_PATH"] = "data/db/db.prod.sqlite" os.makedirs(os.path.dirname(app.config["DB_PATH"]), exist_ok = True) with app.app_context(): @@ -13,10 +18,6 @@ def create_app(): create_tables() run_migrations() - if os.getenv("PYROM_PROD") is None: - app.static_folder = os.path.join(os.path.dirname(__file__), "../data/static") - app.debug = True - from app.routes.app import bp as app_bp app.register_blueprint(app_bp) diff --git a/app/migrations.py b/app/migrations.py index 07a7eca..4523f44 100644 --- a/app/migrations.py +++ b/app/migrations.py @@ -6,13 +6,16 @@ MIGRATIONS = { } def run_migrations(): - print("Running migrations...") - ran = 0 db.execute(""" CREATE TABLE IF NOT EXISTS _migrations( id INTEGER PRIMARY KEY ) """) + if len(MIGRATIONS) == 0: + print("No migrations defined.") + return + print("Running migrations...") + ran = 0 completed = [row["id"] for row in db.query("SELECT id FROM _migrations")] for migration_id in sorted(MIGRATIONS.keys()): if migration_id not in completed: diff --git a/app/schema.py b/app/schema.py index a856aff..8e41fb7 100644 --- a/app/schema.py +++ b/app/schema.py @@ -2,7 +2,91 @@ from .db import db # list of statements SCHEMA = [ + """CREATE TABLE IF NOT EXISTS "api_rate_limits" ( + "id" INTEGER NOT NULL PRIMARY KEY, + "method" TEXT NOT NULL, + "user_id" INTEGER REFERENCES users(id) ON DELETE CASCADE, + "logged_at" INTEGER DEFAULT (unixepoch(CURRENT_TIMESTAMP)) + )""", + """CREATE TABLE IF NOT EXISTS "avatars" ( + "id" INTEGER NOT NULL PRIMARY KEY, + "file_path" TEXT NOT NULL UNIQUE, + "uploaded_at" INTEGER DEFAULT (unixepoch(CURRENT_TIMESTAMP)) + )""", + + """CREATE TABLE IF NOT EXISTS "post_history" ( + "id" INTEGER NOT NULL PRIMARY KEY, + "post_id" INTEGER REFERENCES posts(id) ON DELETE CASCADE, + "content" TEXT NOT NULL, + "edited_at" INTEGER DEFAULT (unixepoch(CURRENT_TIMESTAMP)), + "is_initial_revision" BOOLEAN DEFAULT FALSE + , "original_markup" TEXT NOT NULL, "markup_language" TEXT NOT NULL DEFAULT 'babycode' + )""", + + """CREATE TABLE IF NOT EXISTS "posts" ( + "id" INTEGER NOT NULL PRIMARY KEY, + "thread_id" INTEGER REFERENCES threads(id) ON DELETE CASCADE, + "user_id" INTEGER REFERENCES users(id) ON DELETE SET NULL, + "created_at" INTEGER DEFAULT (unixepoch(CURRENT_TIMESTAMP)), + "current_revision_id" INTEGER REFERENCES post_history(id) + )""", + + """CREATE TABLE IF NOT EXISTS "sessions" ( + "id" INTEGER NOT NULL PRIMARY KEY, + "key" TEXT NOT NULL UNIQUE, + "user_id" INTEGER REFERENCES users(id) ON DELETE CASCADE, + "expires_at" INTEGER NOT NULL, + "created_at" INTEGER DEFAULT (unixepoch(CURRENT_TIMESTAMP)) + )""", + + """CREATE TABLE IF NOT EXISTS "subscriptions" ( + "id" INTEGER NOT NULL PRIMARY KEY, + "user_id" INTEGER REFERENCES users(id) ON DELETE CASCADE, + "thread_id" INTEGER REFERENCES threads(id) ON DELETE CASCADE, + "last_seen" INTEGER DEFAULT (unixepoch(CURRENT_TIMESTAMP)) NOT NULL + )""", + + """CREATE TABLE IF NOT EXISTS "threads" ( + "id" INTEGER NOT NULL PRIMARY KEY, + "topic_id" INTEGER REFERENCES topics(id) ON DELETE CASCADE, + "user_id" INTEGER REFERENCES users(id) ON DELETE SET NULL, + "title" TEXT NOT NULL, + "slug" TEXT NOT NULL UNIQUE, + "created_at" INTEGER DEFAULT (unixepoch(CURRENT_TIMESTAMP)), + "is_locked" BOOLEAN DEFAULT FALSE, "is_stickied" BOOLEAN DEFAULT FALSE + )""", + + """CREATE TABLE IF NOT EXISTS "topics" ( + "id" INTEGER NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "slug" TEXT NOT NULL UNIQUE, + "description" TEXT NOT NULL DEFAULT '', "is_locked" BOOLEAN DEFAULT FALSE, "sort_order" INTEGER NOT NULL DEFAULT 0 + )""", + + """CREATE TABLE IF NOT EXISTS "users" ( + "id" INTEGER NOT NULL PRIMARY KEY, + "username" TEXT NOT NULL UNIQUE, + "password_hash" TEXT NOT NULL, + "permission" INTEGER NOT NULL DEFAULT 0, + "created_at" INTEGER DEFAULT (unixepoch(CURRENT_TIMESTAMP)), + "confirmed_on" INTEGER, "status" TEXT DEFAULT '', + "avatar_id" REFERENCES avatars(id) DEFAULT 1, + "signature_original_markup" TEXT NOT NULL DEFAULT '', + "signature_rendered" TEXT NOT NULL DEFAULT '' + )""", + + # INDEXES + "CREATE INDEX IF NOT EXISTS idx_post_history_post_id ON post_history(post_id)", + "CREATE INDEX IF NOT EXISTS idx_posts_thread ON posts(thread_id, created_at, id)", + "CREATE INDEX IF NOT EXISTS idx_posts_thread_id ON posts(thread_id)", + "CREATE INDEX IF NOT EXISTS idx_rate_limit_user_method ON api_rate_limits (user_id, method)", + "CREATE INDEX IF NOT EXISTS idx_subscription_user_thread ON subscriptions (user_id, thread_id)", + "CREATE INDEX IF NOT EXISTS idx_threads_slug ON threads(slug)", + "CREATE INDEX IF NOT EXISTS idx_threads_topic_id ON threads(topic_id)", + "CREATE INDEX IF NOT EXISTS idx_topics_slug ON topics(slug)", + "CREATE INDEX IF NOT EXISTS session_keys ON sessions(key)", + "CREATE INDEX IF NOT EXISTS sessions_user_id ON sessions(user_id)", ] def create():