-
Notifications
You must be signed in to change notification settings - Fork 0
Streamlit #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Streamlit #2
Changes from all commits
2f63bdd
8118432
493b855
dc941d1
c2e960c
cf4fb88
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,78 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import asyncio | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import json | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import os | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import httpx | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import pandas as pd | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import streamlit as st | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from search import fetch_all_user_details, format_register_time, search_users_api | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from takeout import ( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| API_BASE, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| download_notes_data, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gen_markdown, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| post_process, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| write_user_bak_meta, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| st.title("画吧作品备份工具") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # ── 搜索 ────────────────────────────────────────────── | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| st.header("搜索用户") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keyword = st.text_input("用户名关键词", key="keyword") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if st.button("搜索") and keyword: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def _search(): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async with httpx.AsyncClient(timeout=60) as client: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| users = await search_users_api(client, keyword) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await fetch_all_user_details(client, users) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with st.spinner("搜索中..."): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| users = asyncio.run(_search()) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exact = sorted([u for u in users if u.is_exact_match], key=lambda u: u.notes_count, reverse=True) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| other = sorted([u for u in users if not u.is_exact_match], key=lambda u: u.notes_count, reverse=True) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| st.session_state["search_results"] = exact + other | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if "search_results" in st.session_state: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| users = st.session_state["search_results"] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| df = pd.DataFrame([{ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "精确匹配": "⭐" if u.is_exact_match else "", | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "用户名": u.authorname, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "作品数": u.notes_count, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "注册时间": format_register_time(u.register_time), | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "JID": u.jid, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } for u in users]) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| st.dataframe(df, width='stretch') | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Ovler-Young marked this conversation as resolved.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # ── 导出 ────────────────────────────────────────────── | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| st.header("导出备份") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jid = st.text_input("JID", value=st.session_state.get("export_jid", ""), key="export_jid_input") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from_local = st.checkbox("从本地 notes.json 加载(跳过 API 拉取)") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if st.button("开始导出") and jid: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def _export(): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async with httpx.AsyncClient(timeout=300) as client: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if from_local: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| usr_dir = jid.split("@")[0] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with open(f"user_backups/{usr_dir}/notes.json", "r") as f: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failureCode scanning / CodeQL Uncontrolled data used in path expression High
This path depends on a
user-provided value Error loading related location Loading
Copilot AutofixAI 2 months ago In general, to fix uncontrolled path usage you should (1) define a safe base directory, (2) derive the user-specified component in a restricted/normalized manner, and (3) construct the final path using For this specific code, the best minimal fix is:
All of this can be done directly in
Suggested changeset
1
app.py
Copilot is powered by AI and may make mistakes. Always verify output.
Refresh and try again.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| notes = json.load(f) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+58
to
+60
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| r = await client.get(API_BASE + "notes", params={"jid": jid}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| notes = r.json() | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| write_user_bak_meta(jid, notes) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await download_notes_data(client, jid, notes) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gen_markdown(jid, notes) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with st.spinner("下载中(进度见终端)..."): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| asyncio.run(_export()) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with st.spinner("生成压缩包..."): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| post_process(jid) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| usr_dir = jid.split("@")[0] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| st.success("导出完成!") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| st.write(f"备份预览:https://huabar-takeout-preview.saveweb.org/{usr_dir}/notes.html") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| st.write(f"备份:https://huabar-takeout-preview.saveweb.org/{usr_dir}.zip") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| st.write("您好。请尽快下载压缩包,链接将于数周后失效。祝好。") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,3 +9,23 @@ services: | |
| environment: | ||
| - MONGODB_URI=${MONGODB_URI} | ||
| restart: always | ||
|
|
||
| takeout_ui: | ||
| image: ghcr.io/astral-sh/uv:python3.13-alpine | ||
| container_name: huabar-takeout-ui | ||
| working_dir: /app | ||
| ports: | ||
| - "127.0.0.63:8501:8501" | ||
| volumes: | ||
| - .:/app | ||
| - uv-cache:/root/.cache/uv | ||
| environment: | ||
| - API_BASE=http://draws_index:8080/api/ | ||
| command: sh -c "apk add --no-cache pandoc zip && uv run streamlit run app.py --server.address=0.0.0.0 --server.baseUrlPath=/ui" | ||
| depends_on: | ||
|
Comment on lines
+24
to
+25
|
||
| - draws_index | ||
| restart: always | ||
| mem_limit: 1G | ||
|
|
||
| volumes: | ||
| uv-cache: | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,10 +1,12 @@ | ||||||
| import asyncio | ||||||
| import os | ||||||
|
|
||||||
| import httpx | ||||||
| from datetime import datetime, timezone, timedelta | ||||||
| from typing import List | ||||||
| import csv | ||||||
|
|
||||||
| API_BASE = "http://127.0.0.63:8539/api/" | ||||||
| API_BASE = os.environ.get("API_BASE", "http://127.0.0.63:8539/api/") | ||||||
|
||||||
| API_BASE = os.environ.get("API_BASE", "http://127.0.0.63:8539/api/") | |
| API_BASE = os.environ.get("API_BASE", "http://127.0.0.63:8539/api/").rstrip("/") + "/" |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -14,7 +14,7 @@ | |||||||
| get_urltype | ||||||||
| ) | ||||||||
|
|
||||||||
| API_BASE = "http://127.0.0.63:8539/api/" | ||||||||
| API_BASE = os.environ.get("API_BASE", "http://127.0.0.63:8539/api/") | ||||||||
|
||||||||
| API_BASE = os.environ.get("API_BASE", "http://127.0.0.63:8539/api/") | |
| API_BASE = os.environ.get("API_BASE", "http://127.0.0.63:8539/api/") | |
| API_BASE = API_BASE.rstrip("/") + "/" |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
Uh oh!
There was an error while loading. Please reload this page.