11
This commit is contained in:
@@ -8,7 +8,7 @@ from sqlalchemy.ext.asyncio import async_engine_from_config
|
|||||||
|
|
||||||
from app.config import get_settings
|
from app.config import get_settings
|
||||||
from app.database import Base
|
from app.database import Base
|
||||||
from app.models import CardKey, DesktopConfig, Oem, User # noqa: F401 — 注册元数据
|
from app.models import CardKey, Config, DesktopConfig, Oem, User # noqa: F401 — 注册元数据
|
||||||
|
|
||||||
config = context.config
|
config = context.config
|
||||||
settings = get_settings()
|
settings = get_settings()
|
||||||
|
|||||||
41
alembic/versions/016_create_configs_table.py
Normal file
41
alembic/versions/016_create_configs_table.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
"""create configs table"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
revision: str = "016"
|
||||||
|
down_revision: Union[str, None] = "015"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
op.create_table(
|
||||||
|
"configs",
|
||||||
|
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
|
||||||
|
sa.Column("name", sa.String(length=64), nullable=False),
|
||||||
|
sa.Column("value", sa.Text(), nullable=False),
|
||||||
|
sa.Column("mark", sa.String(length=255), nullable=True),
|
||||||
|
sa.Column(
|
||||||
|
"created_at",
|
||||||
|
sa.DateTime(timezone=True),
|
||||||
|
server_default=sa.text("CURRENT_TIMESTAMP"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column(
|
||||||
|
"updated_at",
|
||||||
|
sa.DateTime(timezone=True),
|
||||||
|
server_default=sa.text("CURRENT_TIMESTAMP"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
sa.UniqueConstraint("name", name="uq_configs_name"),
|
||||||
|
)
|
||||||
|
op.create_index(op.f("ix_configs_name"), "configs", ["name"], unique=False)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.drop_index(op.f("ix_configs_name"), table_name="configs")
|
||||||
|
op.drop_table("configs")
|
||||||
33
app/main.py
33
app/main.py
@@ -7,9 +7,13 @@ from fastapi.staticfiles import StaticFiles
|
|||||||
from app.api.v1.router import api_router
|
from app.api.v1.router import api_router
|
||||||
from app.config import get_settings
|
from app.config import get_settings
|
||||||
from app.core.branding_images import IMAGES_DIR, ensure_images_dir
|
from app.core.branding_images import IMAGES_DIR, ensure_images_dir
|
||||||
|
from app.dependencies import DbSession
|
||||||
from app.middleware.api_crypto import ApiCryptoMiddleware
|
from app.middleware.api_crypto import ApiCryptoMiddleware
|
||||||
from app.redis_client import close_redis, init_redis
|
from app.redis_client import close_redis, init_redis
|
||||||
from app.schemas.common import ApiResponse
|
from app.schemas.common import ApiResponse
|
||||||
|
from app.schemas.release_version import ReleaseVersionOut, SetReleaseVersionIn
|
||||||
|
from app.services import config_store
|
||||||
|
from app.services.config_store import RELEASE_VERSION_KEY
|
||||||
|
|
||||||
settings = get_settings()
|
settings = get_settings()
|
||||||
|
|
||||||
@@ -41,6 +45,35 @@ def create_app() -> FastAPI:
|
|||||||
async def health() -> ApiResponse[dict]:
|
async def health() -> ApiResponse[dict]:
|
||||||
return ApiResponse(ok=True, message="服务正常", data={"status": "up"})
|
return ApiResponse(ok=True, message="服务正常", data={"status": "up"})
|
||||||
|
|
||||||
|
@app.get("/api/checkupdate", response_model=ApiResponse[ReleaseVersionOut])
|
||||||
|
async def check_update(db: DbSession) -> ApiResponse[ReleaseVersionOut]:
|
||||||
|
version = await config_store.get_config_value(db, RELEASE_VERSION_KEY)
|
||||||
|
if not version:
|
||||||
|
return ApiResponse(ok=False, message="未配置 releaseversion")
|
||||||
|
return ApiResponse(
|
||||||
|
ok=True,
|
||||||
|
message="",
|
||||||
|
data=ReleaseVersionOut(version=version),
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.post("/api/setreleaseversion", response_model=ApiResponse[ReleaseVersionOut])
|
||||||
|
async def set_release_version(
|
||||||
|
body: SetReleaseVersionIn,
|
||||||
|
db: DbSession,
|
||||||
|
) -> ApiResponse[ReleaseVersionOut]:
|
||||||
|
version = body.version.strip()
|
||||||
|
saved = await config_store.set_config_value(
|
||||||
|
db,
|
||||||
|
RELEASE_VERSION_KEY,
|
||||||
|
version,
|
||||||
|
mark="桌面端发布版本",
|
||||||
|
)
|
||||||
|
return ApiResponse(
|
||||||
|
ok=True,
|
||||||
|
message="已更新 releaseversion",
|
||||||
|
data=ReleaseVersionOut(version=saved),
|
||||||
|
)
|
||||||
|
|
||||||
ensure_images_dir()
|
ensure_images_dir()
|
||||||
app.mount(
|
app.mount(
|
||||||
"/images",
|
"/images",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from app.models.card_key import CardKey
|
from app.models.card_key import CardKey
|
||||||
|
from app.models.config import Config
|
||||||
from app.models.desktopConfig import DesktopConfig
|
from app.models.desktopConfig import DesktopConfig
|
||||||
from app.models.oem import Oem
|
from app.models.oem import Oem
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
|
|
||||||
__all__ = ["User", "DesktopConfig", "CardKey", "Oem"]
|
__all__ = ["User", "DesktopConfig", "CardKey", "Oem", "Config"]
|
||||||
|
|||||||
18
app/models/config.py
Normal file
18
app/models/config.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
from sqlalchemy import DateTime, String, Text, UniqueConstraint, func
|
||||||
|
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
from app.database import Base
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Config(Base):
|
||||||
|
|
||||||
9
app/schemas/release_version.py
Normal file
9
app/schemas/release_version.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
class ReleaseVersionOut(BaseModel):
|
||||||
|
version: str
|
||||||
|
|
||||||
|
|
||||||
|
class SetReleaseVersionIn(BaseModel):
|
||||||
|
version: str = Field(min_length=1, max_length=64)
|
||||||
34
app/services/config_store.py
Normal file
34
app/services/config_store.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
"""configs 表:全局键值配置(如 releaseversion)。"""
|
||||||
|
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.models.config import Config
|
||||||
|
|
||||||
|
RELEASE_VERSION_KEY = "releaseversion"
|
||||||
|
|
||||||
|
|
||||||
|
async def get_config_value(db: AsyncSession, name: str) -> str | None:
|
||||||
|
result = await db.execute(select(Config).where(Config.name == name))
|
||||||
|
row = result.scalar_one_or_none()
|
||||||
|
return row.value if row else None
|
||||||
|
|
||||||
|
|
||||||
|
async def set_config_value(
|
||||||
|
db: AsyncSession,
|
||||||
|
name: str,
|
||||||
|
value: str,
|
||||||
|
*,
|
||||||
|
mark: str | None = None,
|
||||||
|
) -> str:
|
||||||
|
result = await db.execute(select(Config).where(Config.name == name))
|
||||||
|
row = result.scalar_one_or_none()
|
||||||
|
if row is None:
|
||||||
|
row = Config(name=name, value=value, mark=mark)
|
||||||
|
db.add(row)
|
||||||
|
else:
|
||||||
|
row.value = value
|
||||||
|
if mark is not None:
|
||||||
|
row.mark = mark
|
||||||
|
await db.flush()
|
||||||
|
return row.value
|
||||||
Reference in New Issue
Block a user