diff --git a/core/database.py b/core/database.py index 2330b42..c30471e 100644 --- a/core/database.py +++ b/core/database.py @@ -4,10 +4,10 @@ from sqlalchemy import Boolean, Column, Integer, String, DateTime, JSON, Text, s from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.asyncio import create_async_engine from sqlalchemy.ext.asyncio.session import AsyncSession + from settings import settings engine = create_async_engine(settings.DATABASE_URL) - Base = declarative_base() @@ -33,7 +33,7 @@ class Codes(Base): exp_time = Column(DateTime, nullable=True) -async def init_models(): +async def init_models(s): async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) if await conn.scalar(select(Options).filter(Options.key == 'INSTALL')) is None: @@ -41,6 +41,12 @@ async def init_models(): await conn.execute(delete(table=Options)) await conn.execute(insert(table=Options, values=[ {'key': 'INSTALL', 'value': settings.VERSION}, + {'key': 'DEBUG', 'value': settings.DEBUG}, + {'key': 'DATABASE_FILE', 'value': settings.DATABASE_FILE}, + {'key': 'PORT', 'value': settings.PORT}, + {'key': 'DATA_ROOT', 'value': settings.DATA_ROOT}, + {'key': 'LOCAL_ROOT', 'value': settings.LOCAL_ROOT}, + {'key': 'STATIC_URL', 'value': settings.STATIC_URL}, {'key': 'BANNERS', 'value': settings.BANNERS}, {'key': 'ENABLE_UPLOAD', 'value': settings.ENABLE_UPLOAD}, {'key': 'MAX_DAYS', 'value': settings.MAX_DAYS}, @@ -66,9 +72,12 @@ async def init_models(): f'请尽快修改后台信息!\n' f'FileCodeBox https://github.com/vastsa/FileCodeBox' ) - else: - # 从数据库更新缓存中的setting - await settings.updates(await conn.execute(select(Options).filter())) + await settings.updates(await conn.execute(select(Options).filter())) + + +async def get_config(key): + async with engine.begin() as conn: + return await conn.scalar(select(Options.value).filter(Options.key == key)) async def get_session(): diff --git a/core/storage.py b/core/storage.py index 1d88f4a..51adbd6 100644 --- a/core/storage.py +++ b/core/storage.py @@ -58,6 +58,8 @@ class AliyunFileStorage: upload_filepath = settings.DATA_ROOT + str(now) await asyncio.to_thread(self._save, upload_filepath, file.file) self.upload_file(upload_filepath, remote_filepath) + remote_filepath = remote_filepath.strip(f"https://{settings.BUCKET_NAME}.{settings.OSS_ENDPOINT}/") + self.upload_file(upload_filepath, remote_filepath) await asyncio.to_thread(os.remove, upload_filepath) async def delete_files(self, texts): diff --git a/main.py b/main.py index 809a998..9f05f1b 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,4 @@ +import asyncio import datetime import uuid from pathlib import Path @@ -6,16 +7,25 @@ from sqlalchemy.ext.asyncio import AsyncSession from starlette.requests import Request from starlette.responses import HTMLResponse, FileResponse from starlette.staticfiles import StaticFiles -import os, shutil -from core.utils import error_ip_limit, upload_ip_limit, get_code, storage +import os +import shutil +from core.utils import error_ip_limit, upload_ip_limit, get_code, storage, delete_expire_files from core.depends import admin_required -from settings import settings from fastapi import FastAPI, Depends, UploadFile, Form, File, HTTPException, BackgroundTasks - from core.database import init_models, Options, Codes, get_session +from settings import settings # 实例化FastAPI -app = FastAPI(debug=settings.DEBUG, docs_url=None, redoc_url=None) +app = FastAPI(debug=settings.DEBUG, redoc_url=None, ) + + +@app.on_event('startup') +async def startup(s: AsyncSession = Depends(get_session)): + # 初始化数据库 + await init_models(s) + # 启动后台任务,不定时删除过期文件 + asyncio.create_task(delete_expire_files()) + # 数据存储文件夹 DATA_ROOT = Path(settings.DATA_ROOT) @@ -28,34 +38,27 @@ if not LOCAL_ROOT.exists(): # 静态文件夹,这个固定就行了,静态资源都放在这里 app.mount('/static', StaticFiles(directory='./static'), name="static") + # 首页页面 -index_html = open('templates/index.html', 'r', encoding='utf-8').read() \ - .replace('{{title}}', settings.TITLE) \ - .replace('{{description}}', settings.DESCRIPTION) \ - .replace('{{keywords}}', settings.KEYWORDS) \ - .replace("'{{fileSizeLimit}}'", str(settings.FILE_SIZE_LIMIT)) +index_html = open('templates/index.html', 'r', encoding='utf-8').read() # 管理页面 -admin_html = open('templates/admin.html', 'r', encoding='utf-8').read() \ - .replace('{{title}}', settings.TITLE) \ - .replace('{{description}}', settings.DESCRIPTION) \ - .replace('{{admin_address}}', settings.ADMIN_ADDRESS) \ - .replace('{{keywords}}', settings.KEYWORDS) - - -@app.on_event('startup') -async def startup(): - # 初始化数据库 - await init_models() +admin_html = open('templates/admin.html', 'r', encoding='utf-8').read() @app.get('/') async def index(): - return HTMLResponse(index_html) + return HTMLResponse( + index_html.replace('{{title}}', settings.TITLE).replace('{{description}}', settings.DESCRIPTION).replace( + '{{keywords}}', settings.KEYWORDS).replace("'{{fileSizeLimit}}'", str(settings.FILE_SIZE_LIMIT)) + ) @app.get(f'/{settings.ADMIN_ADDRESS}', description='管理页面') async def admin(): - return HTMLResponse(admin_html) + return HTMLResponse( + admin_html.replace('{{title}}', settings.TITLE).replace('{{description}}', settings.DESCRIPTION).replace( + '{{admin_address}}', settings.ADMIN_ADDRESS).replace('{{keywords}}', settings.KEYWORDS) + ) @app.get(f'/{settings.ADMIN_ADDRESS}/files', dependencies=[Depends(admin_required)]) @@ -150,14 +153,10 @@ async def admin_patch(request: Request, s: AsyncSession = Depends(get_session)): await s.execute(update(Options).where(Options.key == key).values(value=value)) await settings.update(key, value) await s.commit() + await settings.updates([[i.id, i.key, i.value] for i in (await s.execute(select(Options))).scalars().all()]) return {'detail': '修改成功'} -@app.get('/') -async def index(): - return HTMLResponse(index_html) - - @app.post('/') async def index(code: str, ip: str = Depends(error_ip_limit), s: AsyncSession = Depends(get_session)): query = select(Codes).where(Codes.code == code) diff --git a/settings.py b/settings.py index fab7b00..10926ac 100644 --- a/settings.py +++ b/settings.py @@ -1,8 +1,8 @@ import uuid + from starlette.config import Config -# 配置文件.env -# 请将.env移动至data目录,方便docker部署 +# 配置文件.env,存放为data/.env config = Config("data/.env") @@ -65,14 +65,25 @@ class Settings: }] int_dict = {'PORT', 'MAX_DAYS', 'ERROR_COUNT', 'ERROR_MINUTE', 'UPLOAD_COUNT', 'UPLOAD_MINUTE', 'DELETE_EXPIRE_FILES_INTERVAL', 'FILE_SIZE_LIMIT'} + bool_dict = {'DEBUG', 'ENABLE_UPLOAD'} async def update(self, key, value) -> None: if hasattr(self, key): - setattr(self, key, int(value) if key in self.int_dict else value) + if key in self.int_dict: + value = int(value) + elif key in self.bool_dict: + value = value == 'true' + setattr(self, key, value) async def updates(self, options) -> None: - for i, key, value in options: - await self.update(key, value) + with open('data/.env', 'w', encoding='utf-8') as f: + for i, key, value in options: + print(i, key, value) + # 更新env文件 + f.write(f"{key}={value}\n") + # 更新配置 + await self.update(key, value) + f.flush() settings = Settings() diff --git a/templates/admin.html b/templates/admin.html index 7ca3d5f..9493778 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -22,34 +22,6 @@ body { background: #f5f5f5; } - - @media (prefers-color-scheme: dark) { - - body { - background-color: #18181c; - } - - .el-card, .el-textarea__inner, .el-upload-dragger { - border-radius: 20px; - border: 1px solid transparent; - background-color: rgba(255, 255, 255, 0.1); - box-shadow: 5px 5px 0 0 rgba(0, 0, 0, 0.2); - } - - #app * { - color: #ccc !important; - } - - .el-input-group__prepend, .el-menu, .el-menu-item, .el-input__inner, .el-input-group__append, .el-empty__description, .el-select-dropdown, .el-button { - border: 1px solid transparent; - background-color: rgba(0, 0, 0, 0.2); - } - - .el-drawer, #el-drawer__title, .el-drawer__body, .el-drawer__wrapper { - background-color: rgba(0, 0, 0, 0.5); - border: 1px solid transparent; - } - }
@@ -74,7 +46,7 @@