From d786d7f7afa2417d0ade5e41297786f751f29f0e Mon Sep 17 00:00:00 2001 From: OleSTEEP Date: Thu, 2 Oct 2025 22:44:29 +0300 Subject: [PATCH] backend: database and file upload test --- .gitignore | 4 ++- backend/api.py | 48 ++++++++++++++++++++++++++++ backend/db.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++ backend/hmm.txt | 63 +++++++++++++++++++++++++++++++++++++ backend/main.py | 32 ------------------- backend/vntypes.py | 7 ++++- 6 files changed, 198 insertions(+), 34 deletions(-) create mode 100644 backend/api.py create mode 100644 backend/db.py create mode 100644 backend/hmm.txt delete mode 100644 backend/main.py diff --git a/.gitignore b/.gitignore index 260fffd..1c8319a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ venv/ __pycache__/ .idea/ -.DS_Store \ No newline at end of file +.DS_Store +vn_database.db +screens/ \ No newline at end of file diff --git a/backend/api.py b/backend/api.py new file mode 100644 index 0000000..0c20ee7 --- /dev/null +++ b/backend/api.py @@ -0,0 +1,48 @@ +from typing import Annotated +from fastapi import FastAPI, File, UploadFile +from fastapi.middleware.cors import CORSMiddleware +from pathlib import Path + +from vntypes import Novel, Mark +from db import VNDB + +app = FastAPI() +database = VNDB() + +origins = [ + "http://localhost", + "http://localhost:5173", +] + +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +@app.get("/api/posts/{post_id}") +def get_post(post_id: int): + return "NOT IMPLEMENTED!" + +@app.post("/api/new_post") +def new_post(novel: Novel): + print(novel) + return "yay!" + +@app.post("/api/new_mark") +def new_mark(mark: Mark): + database.insert_mark(mark.type, mark.value) + return "yay!" + +@app.post("/api/novel_thumb") +async def novel_thumb(thumb: Annotated[bytes, File()], filename: str): + return {"file_size": len(thumb)} + +@app.post("/api/screenshot") +async def screenshot(scrshot: Annotated[bytes, File()], filename: str): + Path("screens/").mkdir(exist_ok=True) + with open(Path("screens", filename), "wb") as file: + file.write(scrshot) + return {"file_size": len(scrshot)} \ No newline at end of file diff --git a/backend/db.py b/backend/db.py new file mode 100644 index 0000000..473b7cb --- /dev/null +++ b/backend/db.py @@ -0,0 +1,78 @@ +import sqlite3 + +class VNDB: + + def __init__(self, db_name='vn_database.db'): + self.__db_name = db_name + connection = sqlite3.connect(self.__db_name) + cursor = connection.cursor() + + cursor.execute(''' + CREATE TABLE IF NOT EXISTS marks ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + tag TEXT NOT NULL, + value TEXT NOT NULL + ); + ''') + + cursor.execute(''' + CREATE TABLE IF NOT EXISTS authors ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + description TEXT NOT NULL, + thumbnail TEXT NOT NULL + ); + ''') + + cursor.execute(''' + CREATE TABLE IF NOT EXISTS novels ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + author INTEGER NOT NULL + ); + ''') + + cursor.execute(''' + CREATE TABLE IF NOT EXISTS posts_log ( + + novel INTEGER NOT NULL, + + channel INTEGER NOT NULL, + + marks JSON NOT NULL, + + title TEXT NOT NULL, + + description TEXT NOT NULL, + + author INTEGER NOT NULL, + + files_on_disk JSON NOT NULL, + post_info JSON, + post_at INTEGER NOT NULL, + created_at INTEGER NOT NULL + ); + ''') + + connection.commit() + connection.close() + + def __execute(self, sql: str, params: tuple): + connection = sqlite3.connect(self.__db_name) + connection.cursor().execute(sql, params) + connection.commit() + connection.close() + + def insert_mark(self, type: str, value: str): + self.__execute("INSERT INTO marks (tag, value) " + "VALUES (?, ?)", (type, value)) + + def insert_novel(self, title: str, author_id: int): + self.__execute("INSERT INTO novels (title, author) " + "VALUES (?, ?)", (title, author_id)) + + def insert_author(self, title: str, desc: str, thumb: str): + self.__execute("INSERT INTO authors (title, description, thumbnail) " + "VALUES (?, ?, ?)", (title, desc, thumb)) + + \ No newline at end of file diff --git a/backend/hmm.txt b/backend/hmm.txt new file mode 100644 index 0000000..1c2c40c --- /dev/null +++ b/backend/hmm.txt @@ -0,0 +1,63 @@ +-- Эта таблица нужна, чтобы когда человек набирает в поле ввода тег или жанр или бадж, +-- ему подсказывало уже существующие теги/жанры/баджи +CREATE TABLE IF NOT EXISTS mark ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + -- "genre" or "tag" or "badge" + tag TEXT NOT NULL, + -- value, "romance", "Хохлы", as an example + value TEXT NOT NULL +); + +-- Таблица с авторами, для того же - чтобы подсказывало существующего автора. +CREATE TABLE IF NOT EXISTS authors ( + -- ID автора, для кросс-референсов. + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + -- Имя автора. + title TEXT NOT NULL, + -- Описание автора. + description TEXT NOT NULL, + -- Путь к файлу обложки автора. + thumbnail TEXT NOT NULL +); + +-- Сами новеллы. Нужно, поскольку несколько постов могут отсылаться к одной игре. +CREATE TABLE IF NOT EXISTS novels ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + -- Название. Используется исключительно для поиска. + title TEXT NOT NULL, + author INTEGER NOT NULL +); + +-- Таблица с **постами**. +CREATE TABLE IF NOT EXISTS posts_log ( + -- Кросс-референс на id в novels. + novel INTEGER NOT NULL, + + -- ID канала, в который пост будет запощен. + channel INTEGER NOT NULL, + + -- массив idшников на записи в marks. + marks JSON NOT NULL, + + -- Название внки. + title TEXT NOT NULL, + + -- Описание ВНки. + description TEXT NOT NULL, + + -- id автора на момент планирования поста. + author INTEGER NOT NULL, + + -- Мапа, ключ - путь к файлу в фс, значение - + -- `{"post": , "description": "описание файла"}` + -- - ссылка на пост в соответствующем канале с файлами, может + -- быть null. Не должно быть null, если post_info != NULL (то есть если уже запостили). + files_on_disk JSON NOT NULL, + -- не NULL, если пост успешно запостили. + -- {"link": <ссылка на пост>} + post_info JSON, + -- Когда ВН должна быть запощена, second-precise unix timestamp. + post_at INTEGER NOT NULL, + -- Когда запись в бд была создана, second-precise unix timestamp. + created_at INTEGER NOT NULL +); diff --git a/backend/main.py b/backend/main.py deleted file mode 100644 index b2a396b..0000000 --- a/backend/main.py +++ /dev/null @@ -1,32 +0,0 @@ -from fastapi import FastAPI -from fastapi.middleware.cors import CORSMiddleware - -from vntypes import Novel - -app = FastAPI() - -origins = [ - "http://localhost", - "http://localhost:5173", -] - -app.add_middleware( - CORSMiddleware, - allow_origins=origins, - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - -@app.get("/") -def read_root(): - return {"Hello": "World"} - -@app.get("/posts/{uuid}") -def get_post(uuid: str): - return uuid - -@app.post("/new") -def new_post(novel: Novel): - print(novel) - return "yay!" \ No newline at end of file diff --git a/backend/vntypes.py b/backend/vntypes.py index 41a00de..3b843e4 100644 --- a/backend/vntypes.py +++ b/backend/vntypes.py @@ -1,6 +1,11 @@ from pydantic import BaseModel from datetime import datetime +from typing import Literal + +class Mark(BaseModel): + type: Literal["tag", "badge", "genre"] + value: str class Novel(BaseModel): title: str @@ -21,4 +26,4 @@ class FullNovel: upload_queue: list[str] files: list[str] - screenshots: list[str] \ No newline at end of file + screenshots: list[str]