fix:admin
This commit is contained in:
+14
-5
@@ -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():
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
+16
-5
@@ -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()
|
||||
|
||||
+22
-46
@@ -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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -74,7 +46,7 @@
|
||||
<div>次 数:${ file.count }</div>
|
||||
<div>到 期:${ file.exp_time.slice(0,19) }</div>
|
||||
<div v-if="file.type==='text'">
|
||||
<span>内 容:${ file.text }</span>
|
||||
<span style="white-space: pre-line">内 容:${ file.text }</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span>链 接:</span>
|
||||
@@ -121,12 +93,12 @@
|
||||
</el-col>
|
||||
<el-col v-if="activeIndex === '4'">
|
||||
<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">
|
||||
<div style="cursor: pointer;" @click="updateTab=menu.key">${menu.name}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="19">
|
||||
<el-col :span="20">
|
||||
<el-card style="height: 88vh;overflow: scroll">
|
||||
<div v-if="updateTab=='INSTALL'">
|
||||
<div>
|
||||
@@ -158,13 +130,8 @@
|
||||
<div v-else-if="updateTab=='WEBSITE'">
|
||||
<el-form ref="form" style="margin:1rem auto" :model="config" label-width="200px">
|
||||
<el-form-item label="后台密码">
|
||||
<el-input type="password" v-model="config.ADMIN_PASSWORD" placeholder="网站名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-alert
|
||||
title="下列信息修改后需要手动重启系统"
|
||||
type="warning">
|
||||
</el-alert>
|
||||
<el-input type="password" v-model="config.ADMIN_PASSWORD"
|
||||
placeholder="网站名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="网站名称">
|
||||
<el-input v-model="config.TITLE" placeholder="网站名称"></el-input>
|
||||
@@ -175,6 +142,12 @@
|
||||
<el-form-item label="网站关键词">
|
||||
<el-input v-model="config.KEYWORDS" placeholder="网站关键词"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-alert
|
||||
title="下列信息修改后需要手动重启系统"
|
||||
type="warning">
|
||||
</el-alert>
|
||||
</el-form-item>
|
||||
<el-form-item label="后台地址">
|
||||
<el-input v-model="config.ADMIN_ADDRESS" placeholder="后台地址"></el-input>
|
||||
</el-form-item>
|
||||
@@ -272,19 +245,21 @@
|
||||
if (pwd) {
|
||||
login = true;
|
||||
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.getLocalFiles();
|
||||
}
|
||||
},
|
||||
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) {
|
||||
this.paginate.size = value;
|
||||
this.loginAdmin();
|
||||
@@ -327,6 +302,7 @@
|
||||
this.files = res.data.data
|
||||
this.login = true;
|
||||
localStorage.setItem('pwd', this.pwd);
|
||||
this.getConfig();
|
||||
}).catch(e => {
|
||||
localStorage.clear()
|
||||
this.$message({'message': e.response.data.detail, 'type': 'error'});
|
||||
|
||||
Reference in New Issue
Block a user