@ -0,0 +1,55 @@
|
||||
# Node/Vite
|
||||
node_modules/
|
||||
dist/
|
||||
.vite/
|
||||
*.log
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.env
|
||||
*.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# IDE/编辑器
|
||||
.idea/
|
||||
.vscode/
|
||||
*.sublime-workspace
|
||||
*.sublime-project
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# 环境/依赖
|
||||
.env
|
||||
.env.*
|
||||
*.local
|
||||
|
||||
# 依赖锁文件(如不需要提交)
|
||||
# pnpm-lock.yaml
|
||||
|
||||
# Python 数据文件
|
||||
*.sqlite3
|
||||
*.db
|
||||
*.sqlite
|
||||
|
||||
# 其他
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
package-lock.json
|
||||
|
||||
# 个人配置
|
||||
*.iml
|
||||
|
||||
# 备份文件
|
||||
*~
|
||||
*.swp
|
||||
*.bak
|
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="user-scalable=no">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "toupiao_client",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@vueuse/core": "^13.1.0",
|
||||
"axios": "^1.6.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.11",
|
||||
"echarts": "^5.6.0",
|
||||
"element-plus": "^2.9.7",
|
||||
"lodash": "^4.17.21",
|
||||
"pinia": "^2.1.6",
|
||||
"pinia-plugin-persistedstate": "^3.2.0",
|
||||
"vanilla-tilt": "^1.8.1",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"less": "^4.2.0",
|
||||
"sass": "^1.69.7",
|
||||
"vite": "6.2.5"
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
import datetime
|
||||
|
||||
import requests
|
||||
|
||||
data = {
|
||||
'start_datetime':'2025-04-09T15:30:00Z',
|
||||
# 'end_datetime':'2025-04-09T15:30:00Z',
|
||||
}
|
||||
res = requests.post('http://127.0.0.1:9898/api/index', json=data)
|
||||
print(res.json())
|
@ -0,0 +1,30 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from sqlalchemy import URL, create_engine
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||
from sqlalchemy.orm import declarative_base, sessionmaker
|
||||
|
||||
# 加载环境变量
|
||||
load_dotenv()
|
||||
|
||||
# 从环境变量获取数据库配置
|
||||
DB_USER = os.getenv('DB_USER', 'root')
|
||||
DB_PASSWORD = os.getenv('DB_PASSWORD', 'abc123')
|
||||
DB_HOST = os.getenv('DB_HOST', 'localhost')
|
||||
DB_PORT = os.getenv('DB_PORT', '3306')
|
||||
DB_NAME = os.getenv('DB_NAME', 'dashboard')
|
||||
|
||||
uri = f'mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}'
|
||||
|
||||
engine = create_engine(uri)
|
||||
session = sessionmaker(bind=engine)
|
||||
|
||||
async_uri = f'mysql+aiomysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}'
|
||||
async_engine = create_async_engine(async_uri)
|
||||
asyncSession = sessionmaker(
|
||||
bind=async_engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False
|
||||
)
|
||||
|
||||
Base = declarative_base()
|
@ -0,0 +1,126 @@
|
||||
import os
|
||||
import datetime
|
||||
import json
|
||||
|
||||
import requests
|
||||
from jose.constants import ALGORITHMS
|
||||
from jose import jwt
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from server.conf import session
|
||||
from server.models import User
|
||||
|
||||
# 加载环境变量
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class Const:
|
||||
# 从环境变量获取数据库配置
|
||||
DB_USER = os.getenv('DB_USER', 'root')
|
||||
DB_PASSWORD = os.getenv('DB_PASSWORD', 'abc123')
|
||||
DB_HOST = os.getenv('DB_HOST', 'localhost')
|
||||
DB_PORT = os.getenv('DB_PORT', '3306')
|
||||
DB_NAME = os.getenv('DB_NAME', 'dashboard')
|
||||
|
||||
uri = f'mysql+aiomysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}'
|
||||
|
||||
# JWT配置
|
||||
LOGIN_DELTA = datetime.timedelta(hours=int(os.getenv('JWT_EXPIRE_HOURS', '30')))
|
||||
HEADER_PREFIX = 'Bearer '
|
||||
ALGORITHM = os.getenv('JWT_ALGORITHM', ALGORITHMS.HS256)
|
||||
SECRET_KEY = os.getenv('JWT_SECRET_KEY', 'django-insecure-l%mqno@^^-3+k-@7hh$_98r4nwe*qhv9!0bd6n)h(#vv=date2')
|
||||
|
||||
cache = {}
|
||||
|
||||
|
||||
def create_token(payload, delta=datetime.timedelta(days=10)):
|
||||
claims = payload.copy()
|
||||
expire = datetime.datetime.now() + delta
|
||||
# 添加失效时间
|
||||
claims.update({"exp": expire})
|
||||
token = jwt.encode(claims, Const.SECRET_KEY, algorithm=Const.ALGORITHM)
|
||||
return token
|
||||
|
||||
|
||||
def verify_token(token):
|
||||
"""
|
||||
验证token
|
||||
:param token:
|
||||
:return: 返回用户信息
|
||||
"""
|
||||
user = None
|
||||
try:
|
||||
token = token.removeprefix(Const.HEADER_PREFIX)
|
||||
payload = jwt.decode(token, Const.SECRET_KEY, algorithms=Const.ALGORITHM)
|
||||
with session() as db:
|
||||
user = db.query(User).filter_by(userId=payload['userId'], flag=True).first()
|
||||
return user
|
||||
except:
|
||||
pass
|
||||
return user
|
||||
|
||||
|
||||
def bad_res(msg='', code=400, ):
|
||||
return {'code': code, 'message': msg}
|
||||
|
||||
|
||||
def success_res(data=None, msg=None, code=0):
|
||||
return {'code': code, 'message': msg, 'data': data}
|
||||
|
||||
|
||||
def generate_year_month_array():
|
||||
start_year = 2015
|
||||
start_month = 1
|
||||
now = datetime.datetime.now()
|
||||
end_year = now.year
|
||||
end_month = now.month
|
||||
arr = []
|
||||
for year in range(start_year, end_year + 1):
|
||||
arr.append(year)
|
||||
|
||||
# 生成年份-月份数组
|
||||
for year in range(start_year, end_year + 1):
|
||||
month_start = start_month if year == start_year else 1
|
||||
month_end = end_month if year == end_year else 12
|
||||
for month in range(month_start, month_end + 1):
|
||||
arr.append(f"{year}-{month:02d}")
|
||||
|
||||
return arr
|
||||
|
||||
|
||||
class OpenDigger:
|
||||
def __init__(self):
|
||||
self.url = 'https://api.github.com/users/{username}/repos?per_page=100&page=1'
|
||||
self.headers = {
|
||||
'Authorization': f'Bearer {os.getenv("GITHUB_TOKEN", "")}'
|
||||
}
|
||||
|
||||
def get_user_repos(self, username, platform='github'):
|
||||
res = {
|
||||
'total': 0,
|
||||
'avatar': 0,
|
||||
}
|
||||
try:
|
||||
if platform == 'github':
|
||||
url = self.url.format(username=username)
|
||||
all_repos = []
|
||||
page = 1
|
||||
while True:
|
||||
resJ = requests.get(url, headers=self.headers, params={"per_page": 100, "page": page})
|
||||
repos = resJ.json()
|
||||
if not repos:
|
||||
break
|
||||
all_repos.extend(repos)
|
||||
page += 1
|
||||
print(len(all_repos))
|
||||
total_stars = sum(repo["stargazers_count"] for repo in all_repos)
|
||||
total_forks = sum(repo["forks_count"] for repo in all_repos)
|
||||
print(total_stars)
|
||||
print(total_forks)
|
||||
else:
|
||||
return {}
|
||||
except:
|
||||
return {}
|
||||
|
||||
|
||||
openDigger = OpenDigger()
|
@ -0,0 +1,47 @@
|
||||
import asyncio
|
||||
|
||||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from starlette.middleware import Middleware
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse,StreamingResponse
|
||||
from starlette.staticfiles import StaticFiles
|
||||
|
||||
from server.const import verify_token
|
||||
from server.urls import router
|
||||
import os
|
||||
|
||||
class CustomMiddleware(BaseHTTPMiddleware):
|
||||
async def dispatch(self, request: Request, call_next):
|
||||
# 排除 docs 和 redoc 路径
|
||||
if request.url.path in ['/docs', '/redoc', '/openapi.json']:
|
||||
return await call_next(request)
|
||||
if request.url.path.split('/')[-1][0].islower() and 'static' not in request.url.path:
|
||||
token = request.headers.get('token')
|
||||
user = verify_token(token)
|
||||
|
||||
request.state.user = user
|
||||
if user is None:
|
||||
return JSONResponse({'code': 401, 'message': '请重新登录'})
|
||||
|
||||
response = await call_next(request)
|
||||
return response
|
||||
|
||||
|
||||
middlewares = [
|
||||
Middleware(CORSMiddleware, allow_origins=['*']),
|
||||
Middleware(CustomMiddleware)
|
||||
|
||||
]
|
||||
|
||||
fast_app = FastAPI(middleware=middlewares)
|
||||
fast_app.include_router(router, prefix='/api')
|
||||
|
||||
static_dir = os.path.join(os.path.dirname(__file__), "static")
|
||||
fast_app.mount("/static", StaticFiles(directory=static_dir), name="static")
|
||||
|
||||
if __name__ == '__main__':
|
||||
port = 7878
|
||||
uvicorn.run('main:fast_app', port=port, reload=True)
|
After Width: | Height: | Size: 104 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 118 KiB |
After Width: | Height: | Size: 7.7 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 326 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 203 KiB |
After Width: | Height: | Size: 104 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 192 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 260 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 164 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 125 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 212 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 921 KiB |
After Width: | Height: | Size: 157 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 155 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 39 KiB |