feat: add wildcard token support for allowed extensions and access token CLI commands

Introduce WILDCARD_TOKENS set in attachments.py to treat "*", ".*", "*.*" as wildcards that fall back to ALLOWED_UPLOAD_TYPES. Add cmd_token_issue and cmd_token_list CLI commands for issuing and listing DevPlace access tokens. Register access_tokens table in SOFT_DELETE_TABLES and initialize its columns and indexes in init_db. Replace Form() with Depends(json_or_form(...)) in admin backup, container, notification, settings, and user routers to support both JSON and form data.
This commit is contained in:
2026-06-17 17:10:52 +00:00
parent 217210e02f
commit 9598a1b867
45 changed files with 436 additions and 284 deletions
+3 -10
View File
@@ -2,7 +2,7 @@
import logging
from typing import Annotated
from fastapi import APIRouter, Request, Form
from fastapi import Depends, APIRouter, Request
from devplacepy.models import ProfileForm
from fastapi.responses import HTMLResponse, JSONResponse
from devplacepy.database import (
@@ -52,11 +52,11 @@ from devplacepy.services.ai_modifier import schedule_modification
from devplacepy.config import DEFAULT_CORRECTION_PROMPT, DEFAULT_MODIFIER_PROMPT
from devplacepy.routers.profile.usage import _ai_quota, _correction_usage, _modifier_usage
from devplacepy.dependencies import json_or_form
logger = logging.getLogger(__name__)
router = APIRouter()
@router.get("/search")
async def search_users(request: Request, q: str = ""):
require_user_api(request)
@@ -65,7 +65,6 @@ async def search_users(request: Request, q: str = ""):
results = search_users_by_username(q)
return JSONResponse({"results": results})
def _follow_people(profile_uid: str, mode: str, current_user, page: int):
people, pagination = get_follow_list(profile_uid, mode, page)
mine = (
@@ -78,17 +77,14 @@ def _follow_people(profile_uid: str, mode: str, current_user, page: int):
person["is_self"] = bool(current_user and current_user["uid"] == person["uid"])
return people, pagination
@router.get("/{username}/followers")
async def profile_followers(request: Request, username: str, page: int = 1):
return _follow_list_json(request, username, "followers", page)
@router.get("/{username}/following")
async def profile_following(request: Request, username: str, page: int = 1):
return _follow_list_json(request, username, "following", page)
def _follow_list_json(request: Request, username: str, mode: str, page: int):
profile_user = get_table("users").find_one(username=username)
if not profile_user:
@@ -114,7 +110,6 @@ def _follow_list_json(request: Request, username: str, mode: str, page: int):
}
)
@router.get("/{username}", response_class=HTMLResponse)
async def profile_page(
request: Request, username: str, tab: str = "posts", page: int = 1
@@ -374,9 +369,8 @@ async def profile_page(
model=ProfileOut,
)
@router.post("/update")
async def update_profile(request: Request, data: Annotated[ProfileForm, Form()]):
async def update_profile(request: Request, data: Annotated[ProfileForm, Depends(json_or_form(ProfileForm))]):
user = require_user(request)
users = get_table("users")
users.update(
@@ -412,7 +406,6 @@ async def update_profile(request: Request, data: Annotated[ProfileForm, Form()])
track_action(user["uid"], "profile")
return action_result(request, f"/profile/{user['username']}")
@router.post("/regenerate-api-key")
async def regenerate_api_key(request: Request):
user = require_user(request)