Remove project OAuth hardcodes

This commit is contained in:
Your Name
2026-08-03 17:23:51 +05:00
parent cf12aa0df8
commit f2effe002d
6 changed files with 118 additions and 16 deletions
+44 -13
View File
@@ -43,7 +43,7 @@ VK_OAUTH_VERIFIER_COOKIE = "vk_oauth_verifier"
VK_OAUTH_STATE_COOKIE = "vk_oauth_state"
BASE_DIR = Path(__file__).resolve().parent
PROJECT_ROOT = BASE_DIR.parents[1]
FAVICON_PATH = PROJECT_ROOT / "favi.png"
FAVICON_PATH = Path(settings.admin_favicon_path).resolve() if settings.admin_favicon_path.strip() else PROJECT_ROOT / "favi.png"
templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
app = FastAPI(title=settings.display_site_title)
LOCAL_TZ = ZoneInfo("Asia/Yekaterinburg")
@@ -54,6 +54,22 @@ EDITOR_MEDIA_DIR.mkdir(parents=True, exist_ok=True)
app.mount("/uploads", StaticFiles(directory=str(UPLOAD_ROOT)), name="uploads")
def public_origin(request: Request) -> str:
configured = settings.vk_oauth_origin.strip().rstrip("/")
if configured:
return configured
proto = request.headers.get("x-forwarded-proto") or request.url.scheme
host = request.headers.get("x-forwarded-host") or request.headers.get("host") or request.url.netloc
return f"{proto}://{host}".rstrip("/")
def oauth_redirect_uri(request: Request, path: str) -> str:
configured = settings.vk_oauth_redirect_uri.strip()
if configured and path == "/vk-oauth/callback":
return configured
return f"{public_origin(request)}{path}"
@app.get("/favi.png")
async def favicon_png():
return FileResponse(FAVICON_PATH)
@@ -882,7 +898,15 @@ def parse_source_line(line: str) -> dict[str, str]:
if not raw:
return {"name": "", "tag": "", "url": "", "error": "Пустая строка"}
parts = re.split(r"\s+", raw)
url_index = next((i for i, part in enumerate(parts) if "vk.com/" in part or part.startswith("club")), -1)
url_index = next(
(
i
for i, part in enumerate(parts)
if any(domain in part for domain in ("vk.com/", "vk.ru/", "m.vk.com/"))
or part.lower().startswith(("club", "public"))
),
-1,
)
if url_index < 0:
if len(parts) == 1:
value = parts[0].strip()
@@ -900,8 +924,9 @@ def parse_source_line(line: str) -> dict[str, str]:
else:
name = ""
tag = ""
if url.startswith("vk.com/"):
if url.startswith(("vk.com/", "vk.ru/", "m.vk.com/")):
url = f"https://{url}"
url = url.replace("https://vk.ru/", "https://vk.com/", 1).replace("https://m.vk.com/", "https://vk.com/", 1)
return {"name": name, "tag": normalize_hash_tag(tag, ""), "url": url, "error": ""}
@@ -1757,19 +1782,22 @@ async def vk_oauth_callback(request: Request, code: str = "", error: str = "", e
@app.get("/vk/oauth/start")
async def vk_oauth_start() -> RedirectResponse:
async def vk_oauth_start(request: Request) -> RedirectResponse:
client_id = settings.vk_oauth_client_id.strip() or str(await fetch_setting("vk_poster_app_id", "") or "").strip()
if not client_id:
return redirect("/workers")
verifier = secrets.token_urlsafe(64)
state = secrets.token_urlsafe(24)
redirect_uri = "https://sw.exostring.xyz/vk/oauth/callback"
redirect_uri = oauth_redirect_uri(request, "/vk/oauth/callback")
params = {
"client_id": "54635120",
"client_id": client_id,
"redirect_uri": redirect_uri,
"response_type": "code",
"scope": "wall photos video groups offline",
"state": state,
"code_challenge": pkce_challenge(verifier),
"code_challenge_method": "s256",
"origin": "https://sw.exostring.xyz",
"origin": public_origin(request),
"v": "5.199",
}
response = RedirectResponse(f"https://id.vk.ru/authorize?{urlencode(params)}")
@@ -1779,13 +1807,16 @@ async def vk_oauth_start() -> RedirectResponse:
@app.get("/vk/group-oauth/start")
async def vk_group_oauth_start() -> RedirectResponse:
async def vk_group_oauth_start(request: Request) -> RedirectResponse:
client_id = settings.vk_oauth_client_id.strip() or str(await fetch_setting("vk_poster_app_id", "") or "").strip()
if not client_id:
return redirect("/workers")
group_id = abs(int(settings.vk_storage_group_id))
params = {
"client_id": "54635120",
"client_id": client_id,
"group_ids": str(group_id),
"display": "page",
"redirect_uri": "https://sw.exostring.xyz/vk/oauth/callback",
"redirect_uri": oauth_redirect_uri(request, "/vk/oauth/callback"),
"scope": "manage,photos,docs",
"response_type": "token",
"state": secrets.token_urlsafe(24),
@@ -1804,7 +1835,7 @@ async def vk_poster_oauth_start(request: Request) -> RedirectResponse:
return redirect("/workers")
verifier = secrets.token_urlsafe(64)
state = secrets.token_urlsafe(24)
redirect_uri = "https://sw.exostring.xyz/vk-oauth/callback"
redirect_uri = oauth_redirect_uri(request, "/vk-oauth/callback")
params = {
"client_id": client_id,
"redirect_uri": redirect_uri,
@@ -1813,7 +1844,7 @@ async def vk_poster_oauth_start(request: Request) -> RedirectResponse:
"state": state,
"code_challenge": pkce_challenge(verifier),
"code_challenge_method": "s256",
"origin": "https://sw.exostring.xyz",
"origin": public_origin(request),
"v": "5.199",
}
response = RedirectResponse(f"https://id.vk.ru/authorize?{urlencode(params)}")
@@ -1850,7 +1881,7 @@ async def vk_poster_oauth_callback(
client_id = str(await fetch_setting("vk_poster_app_id", "") or "").strip()
client_secret = str(await fetch_setting("vk_poster_client_secret", "") or "").strip()
owner_id = int(await fetch_int_setting("vk_poster_owner_id", 0))
redirect_uri = "https://sw.exostring.xyz/vk-oauth/callback"
redirect_uri = oauth_redirect_uri(request, "/vk-oauth/callback")
code_verifier = request.cookies.get(VK_OAUTH_VERIFIER_COOKIE, "")
device_id = request.query_params.get("device_id", "")
try:
+4
View File
@@ -8,6 +8,7 @@ class Settings(BaseSettings):
app_secret_key: str = "change-me"
admin_site_title: str = ""
admin_app_title: str = "Редакторская"
admin_favicon_path: str = ""
admin_bootstrap_login: str = "admin"
admin_bootstrap_password: str = ""
@@ -21,6 +22,9 @@ class Settings(BaseSettings):
vk_group_access_token: str = ""
vk_api_version: str = "5.199"
vk_storage_group_id: int = 0
vk_oauth_client_id: str = ""
vk_oauth_redirect_uri: str = ""
vk_oauth_origin: str = ""
tg_bot_token: str = ""
tg_media_channel_id: str = ""
+18
View File
@@ -393,4 +393,22 @@
</details>
{% endfor %}
</div>
<script>
(() => {
const scrollKey = "workers-scroll-y";
const restoreY = sessionStorage.getItem(scrollKey);
if (restoreY !== null) {
sessionStorage.removeItem(scrollKey);
requestAnimationFrame(() => window.scrollTo(0, Number(restoreY) || 0));
}
document.addEventListener("submit", (event) => {
const form = event.target;
if (!(form instanceof HTMLFormElement)) return;
const action = form.getAttribute("action") || "";
if (action.startsWith("/workers") || action.startsWith("/settings") || action.includes("-schedule/")) {
sessionStorage.setItem(scrollKey, String(window.scrollY));
}
}, true);
})();
</script>
{% endblock %}
+4 -2
View File
@@ -192,8 +192,10 @@ def normalize_vk_source(value: str) -> str:
raw = str(value or "").strip()
if not raw:
return ""
if "vk.com" in raw:
raw = raw.split("vk.com", 1)[1]
for host in ("vk.com", "vk.ru", "m.vk.com"):
if host in raw:
raw = raw.split(host, 1)[1]
break
raw = raw.lstrip("/")
raw = raw.split("?", 1)[0].split("#", 1)[0].strip()
raw = re.sub(r"^(club|public)", "", raw, flags=re.IGNORECASE)