@ -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 |