fix:admin

This commit is contained in:
lan
2023-01-16 20:20:46 +08:00
parent be5c043d52
commit d4c3fda37c
5 changed files with 81 additions and 84 deletions
+14 -5
View File
@@ -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.declarative import declarative_base
from sqlalchemy.ext.asyncio import create_async_engine from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.ext.asyncio.session import AsyncSession from sqlalchemy.ext.asyncio.session import AsyncSession
from settings import settings from settings import settings
engine = create_async_engine(settings.DATABASE_URL) engine = create_async_engine(settings.DATABASE_URL)
Base = declarative_base() Base = declarative_base()
@@ -33,7 +33,7 @@ class Codes(Base):
exp_time = Column(DateTime, nullable=True) exp_time = Column(DateTime, nullable=True)
async def init_models(): async def init_models(s):
async with engine.begin() as conn: async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all) await conn.run_sync(Base.metadata.create_all)
if await conn.scalar(select(Options).filter(Options.key == 'INSTALL')) is None: 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(delete(table=Options))
await conn.execute(insert(table=Options, values=[ await conn.execute(insert(table=Options, values=[
{'key': 'INSTALL', 'value': settings.VERSION}, {'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': 'BANNERS', 'value': settings.BANNERS},
{'key': 'ENABLE_UPLOAD', 'value': settings.ENABLE_UPLOAD}, {'key': 'ENABLE_UPLOAD', 'value': settings.ENABLE_UPLOAD},
{'key': 'MAX_DAYS', 'value': settings.MAX_DAYS}, {'key': 'MAX_DAYS', 'value': settings.MAX_DAYS},
@@ -66,9 +72,12 @@ async def init_models():
f'请尽快修改后台信息!\n' f'请尽快修改后台信息!\n'
f'FileCodeBox https://github.com/vastsa/FileCodeBox' f'FileCodeBox https://github.com/vastsa/FileCodeBox'
) )
else: await settings.updates(await conn.execute(select(Options).filter()))
# 从数据库更新缓存中的setting
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(): async def get_session():
+2
View File
@@ -58,6 +58,8 @@ class AliyunFileStorage:
upload_filepath = settings.DATA_ROOT + str(now) upload_filepath = settings.DATA_ROOT + str(now)
await asyncio.to_thread(self._save, upload_filepath, file.file) await asyncio.to_thread(self._save, upload_filepath, file.file)
self.upload_file(upload_filepath, remote_filepath) 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) await asyncio.to_thread(os.remove, upload_filepath)
async def delete_files(self, texts): async def delete_files(self, texts):
+27 -28
View File
@@ -1,3 +1,4 @@
import asyncio
import datetime import datetime
import uuid import uuid
from pathlib import Path from pathlib import Path
@@ -6,16 +7,25 @@ from sqlalchemy.ext.asyncio import AsyncSession
from starlette.requests import Request from starlette.requests import Request
from starlette.responses import HTMLResponse, FileResponse from starlette.responses import HTMLResponse, FileResponse
from starlette.staticfiles import StaticFiles from starlette.staticfiles import StaticFiles
import os, shutil import os
from core.utils import error_ip_limit, upload_ip_limit, get_code, storage 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 core.depends import admin_required
from settings import settings
from fastapi import FastAPI, Depends, UploadFile, Form, File, HTTPException, BackgroundTasks from fastapi import FastAPI, Depends, UploadFile, Form, File, HTTPException, BackgroundTasks
from core.database import init_models, Options, Codes, get_session from core.database import init_models, Options, Codes, get_session
from settings import settings
# 实例化FastAPI # 实例化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) DATA_ROOT = Path(settings.DATA_ROOT)
@@ -28,34 +38,27 @@ if not LOCAL_ROOT.exists():
# 静态文件夹,这个固定就行了,静态资源都放在这里 # 静态文件夹,这个固定就行了,静态资源都放在这里
app.mount('/static', StaticFiles(directory='./static'), name="static") app.mount('/static', StaticFiles(directory='./static'), name="static")
# 首页页面 # 首页页面
index_html = open('templates/index.html', 'r', encoding='utf-8').read() \ 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))
# 管理页面 # 管理页面
admin_html = open('templates/admin.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()
@app.get('/') @app.get('/')
async def index(): 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='管理页面') @app.get(f'/{settings.ADMIN_ADDRESS}', description='管理页面')
async def admin(): 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)]) @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 s.execute(update(Options).where(Options.key == key).values(value=value))
await settings.update(key, value) await settings.update(key, value)
await s.commit() await s.commit()
await settings.updates([[i.id, i.key, i.value] for i in (await s.execute(select(Options))).scalars().all()])
return {'detail': '修改成功'} return {'detail': '修改成功'}
@app.get('/')
async def index():
return HTMLResponse(index_html)
@app.post('/') @app.post('/')
async def index(code: str, ip: str = Depends(error_ip_limit), s: AsyncSession = Depends(get_session)): async def index(code: str, ip: str = Depends(error_ip_limit), s: AsyncSession = Depends(get_session)):
query = select(Codes).where(Codes.code == code) query = select(Codes).where(Codes.code == code)
+16 -5
View File
@@ -1,8 +1,8 @@
import uuid import uuid
from starlette.config import Config from starlette.config import Config
# 配置文件.env # 配置文件.env,存放为data/.env
# 请将.env移动至data目录,方便docker部署
config = Config("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', int_dict = {'PORT', 'MAX_DAYS', 'ERROR_COUNT', 'ERROR_MINUTE', 'UPLOAD_COUNT', 'UPLOAD_MINUTE',
'DELETE_EXPIRE_FILES_INTERVAL', 'FILE_SIZE_LIMIT'} 'DELETE_EXPIRE_FILES_INTERVAL', 'FILE_SIZE_LIMIT'}
bool_dict = {'DEBUG', 'ENABLE_UPLOAD'}
async def update(self, key, value) -> None: async def update(self, key, value) -> None:
if hasattr(self, key): 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: async def updates(self, options) -> None:
for i, key, value in options: with open('data/.env', 'w', encoding='utf-8') as f:
await self.update(key, value) 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() settings = Settings()
+22 -46
View File
@@ -22,34 +22,6 @@
body { body {
background: #f5f5f5; 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;
}
}
</style> </style>
</head> </head>
<body> <body>
@@ -74,7 +46,7 @@
<div>&nbsp;&nbsp; ${ file.count }</div> <div>&nbsp;&nbsp; ${ file.count }</div>
<div>&nbsp;&nbsp; ${ file.exp_time.slice(0,19) }</div> <div>&nbsp;&nbsp; ${ file.exp_time.slice(0,19) }</div>
<div v-if="file.type==='text'"> <div v-if="file.type==='text'">
<span>&nbsp;&nbsp; ${ file.text }</span> <span style="white-space: pre-line">&nbsp;&nbsp; ${ file.text }</span>
</div> </div>
<div v-else> <div v-else>
<span>&nbsp;&nbsp; </span> <span>&nbsp;&nbsp; </span>
@@ -121,12 +93,12 @@
</el-col> </el-col>
<el-col v-if="activeIndex === '4'"> <el-col v-if="activeIndex === '4'">
<el-row> <el-row>
<el-col :span="5" style="height: 88vh;overflow: scroll" :gutter="10"> <el-col :span="4" style="height: 88vh;overflow: scroll;text-align: center" :gutter="10">
<el-card v-for="menu in menus" :key="menu.key"> <el-card v-for="menu in menus" :key="menu.key">
<div style="cursor: pointer;" @click="updateTab=menu.key">${menu.name}</div> <div style="cursor: pointer;" @click="updateTab=menu.key">${menu.name}</div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="19"> <el-col :span="20">
<el-card style="height: 88vh;overflow: scroll"> <el-card style="height: 88vh;overflow: scroll">
<div v-if="updateTab=='INSTALL'"> <div v-if="updateTab=='INSTALL'">
<div> <div>
@@ -158,13 +130,8 @@
<div v-else-if="updateTab=='WEBSITE'"> <div v-else-if="updateTab=='WEBSITE'">
<el-form ref="form" style="margin:1rem auto" :model="config" label-width="200px"> <el-form ref="form" style="margin:1rem auto" :model="config" label-width="200px">
<el-form-item label="后台密码"> <el-form-item label="后台密码">
<el-input type="password" v-model="config.ADMIN_PASSWORD" placeholder="网站名称"></el-input> <el-input type="password" v-model="config.ADMIN_PASSWORD"
</el-form-item> placeholder="网站名称"></el-input>
<el-form-item>
<el-alert
title="下列信息修改后需要手动重启系统"
type="warning">
</el-alert>
</el-form-item> </el-form-item>
<el-form-item label="网站名称"> <el-form-item label="网站名称">
<el-input v-model="config.TITLE" placeholder="网站名称"></el-input> <el-input v-model="config.TITLE" placeholder="网站名称"></el-input>
@@ -175,6 +142,12 @@
<el-form-item label="网站关键词"> <el-form-item label="网站关键词">
<el-input v-model="config.KEYWORDS" placeholder="网站关键词"></el-input> <el-input v-model="config.KEYWORDS" placeholder="网站关键词"></el-input>
</el-form-item> </el-form-item>
<el-form-item>
<el-alert
title="下列信息修改后需要手动重启系统"
type="warning">
</el-alert>
</el-form-item>
<el-form-item label="后台地址"> <el-form-item label="后台地址">
<el-input v-model="config.ADMIN_ADDRESS" placeholder="后台地址"></el-input> <el-input v-model="config.ADMIN_ADDRESS" placeholder="后台地址"></el-input>
</el-form-item> </el-form-item>
@@ -272,19 +245,21 @@
if (pwd) { if (pwd) {
login = true; login = true;
this.pwd = pwd; this.pwd = pwd;
axios.get(window.location.href + '/config', {
headers: {
pwd: pwd
}
}).then(res => {
this.config = res.data.data;
this.menus = res.data.menus;
});
this.loginAdmin(); this.loginAdmin();
this.getLocalFiles(); this.getLocalFiles();
} }
}, },
methods: { methods: {
getConfig: function () {
axios.get(window.location.href + '/config', {
headers: {
pwd: this.pwd
}
}).then(res => {
this.config = res.data.data;
this.menus = res.data.menus;
});
},
updateSize: function (value) { updateSize: function (value) {
this.paginate.size = value; this.paginate.size = value;
this.loginAdmin(); this.loginAdmin();
@@ -327,6 +302,7 @@
this.files = res.data.data this.files = res.data.data
this.login = true; this.login = true;
localStorage.setItem('pwd', this.pwd); localStorage.setItem('pwd', this.pwd);
this.getConfig();
}).catch(e => { }).catch(e => {
localStorage.clear() localStorage.clear()
this.$message({'message': e.response.data.detail, 'type': 'error'}); this.$message({'message': e.response.data.detail, 'type': 'error'});