Add immutable revisions, PGVN editor, asset replacement, merge and rebase workflows, authenticated media delivery and reachability GC. Harden the script interpreter, pin unrpyc and remove tracked build metadata. Include current compatibility checks, Naninovel import and regression tests.
136 lines
5.9 KiB
Python
136 lines
5.9 KiB
Python
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from sqlalchemy import (
|
|
ARRAY,
|
|
BigInteger,
|
|
Boolean,
|
|
DateTime,
|
|
ForeignKey,
|
|
Integer,
|
|
String,
|
|
Text,
|
|
UniqueConstraint,
|
|
func,
|
|
)
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from app.db import Base
|
|
|
|
|
|
class Role(Base):
|
|
__tablename__ = "roles"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
name: Mapped[str] = mapped_column(String(64), unique=True)
|
|
permissions: Mapped[list[str]] = mapped_column(ARRAY(String), default=list)
|
|
is_system: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
|
|
|
|
class User(Base):
|
|
__tablename__ = "users"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
username: Mapped[str] = mapped_column(String(64), unique=True)
|
|
password_hash: Mapped[str] = mapped_column(String(255))
|
|
role_id: Mapped[int] = mapped_column(ForeignKey("roles.id"))
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
must_change_password: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
must_change_username: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
token_version: Mapped[int] = mapped_column(Integer, default=0)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
role: Mapped[Role] = relationship(lazy="joined")
|
|
|
|
@property
|
|
def permissions(self) -> list[str]:
|
|
return sorted(self.role.permissions) if self.is_active else []
|
|
|
|
|
|
class Game(Base):
|
|
__tablename__ = "games"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
title: Mapped[str] = mapped_column(String(255))
|
|
description: Mapped[str] = mapped_column(Text, default="")
|
|
tags: Mapped[list[str]] = mapped_column(ARRAY(String), default=list)
|
|
engine: Mapped[str] = mapped_column(String(32), default="renpy")
|
|
cover_hash: Mapped[str | None] = mapped_column(String(80))
|
|
# the version "Play" opens; translations are published by merging into it
|
|
main_version_id: Mapped[int | None] = mapped_column(
|
|
ForeignKey("game_versions.id", ondelete="SET NULL", use_alter=True))
|
|
created_by_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
created_by: Mapped[User | None] = relationship(lazy="joined")
|
|
|
|
|
|
class GameVersion(Base):
|
|
__tablename__ = "game_versions"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
game_id: Mapped[int] = mapped_column(ForeignKey("games.id", ondelete="CASCADE"), index=True)
|
|
parent_id: Mapped[int | None] = mapped_column(ForeignKey("game_versions.id", ondelete="SET NULL"))
|
|
owner_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"))
|
|
name: Mapped[str] = mapped_column(String(255))
|
|
description: Mapped[str] = mapped_column(Text, default="")
|
|
# uploading -> queued -> converting -> ready | failed; importing = app.import_dir run
|
|
status: Mapped[str] = mapped_column(String(16), default="uploading", index=True)
|
|
# draft: visible to the owner and reviewers only
|
|
visibility: Mapped[str] = mapped_column(String(16), default="published")
|
|
kind: Mapped[str] = mapped_column(String(16), default="original") # original|translation|mod
|
|
progress: Mapped[str] = mapped_column(String(255), default="")
|
|
upload_name: Mapped[str | None] = mapped_column(String(255))
|
|
manifest: Mapped[dict[str, Any] | None] = mapped_column(JSONB)
|
|
revision_id: Mapped[str | None] = mapped_column(String(36))
|
|
base_revision_id: Mapped[str | None] = mapped_column(String(36))
|
|
report: Mapped[dict[str, Any] | None] = mapped_column(JSONB)
|
|
size_bytes: Mapped[int] = mapped_column(BigInteger, default=0)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
owner: Mapped[User | None] = relationship(lazy="joined")
|
|
|
|
|
|
class VersionHistory(Base):
|
|
"""Previous manifests of a version that was changed in place (e.g. a merged translation)."""
|
|
__tablename__ = "version_history"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
version_id: Mapped[int] = mapped_column(ForeignKey("game_versions.id", ondelete="CASCADE"),
|
|
index=True)
|
|
user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"))
|
|
note: Mapped[str] = mapped_column(String(255), default="")
|
|
manifest: Mapped[dict[str, Any]] = mapped_column(JSONB)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
class Revision(Base):
|
|
"""Immutable snapshots retained independently of deletable version cards."""
|
|
__tablename__ = "revisions"
|
|
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True)
|
|
game_id: Mapped[int] = mapped_column(ForeignKey("games.id", ondelete="CASCADE"), index=True)
|
|
user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"))
|
|
parents: Mapped[list[str]] = mapped_column(ARRAY(String), default=list)
|
|
manifest: Mapped[dict[str, Any]] = mapped_column(JSONB)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
class Save(Base):
|
|
__tablename__ = "saves"
|
|
__table_args__ = (UniqueConstraint("user_id", "version_id", "slot"),)
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
|
|
version_id: Mapped[int] = mapped_column(ForeignKey("game_versions.id", ondelete="CASCADE"))
|
|
slot: Mapped[str] = mapped_column(String(32))
|
|
title: Mapped[str] = mapped_column(String(255), default="")
|
|
data: Mapped[dict[str, Any]] = mapped_column(JSONB)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
|
)
|
|
|
|
|
|
from app import revisions as _revisions # noqa: E402,F401
|