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.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():
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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>次 数:${ file.count }</div>
|
<div>次 数:${ file.count }</div>
|
||||||
<div>到 期:${ file.exp_time.slice(0,19) }</div>
|
<div>到 期:${ file.exp_time.slice(0,19) }</div>
|
||||||
<div v-if="file.type==='text'">
|
<div v-if="file.type==='text'">
|
||||||
<span>内 容:${ file.text }</span>
|
<span style="white-space: pre-line">内 容:${ file.text }</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<span>链 接:</span>
|
<span>链 接:</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'});
|
||||||
|
|||||||
Reference in New Issue
Block a user