aurweb/aurweb/schema.py
Leonidas Spyropoulos 6ede837b4f
feat: allow users to hide deleted comments
Closes: #435

Signed-off-by: Leonidas Spyropoulos <artafinde@archlinux.org>
2023-04-24 09:13:38 +01:00

619 lines
19 KiB
Python

"""
Schema of aurweb's database.
Changes here should always be accompanied by an Alembic migration, which can be
usually be automatically generated. See `migrations/README` for details.
"""
from sqlalchemy import (
CHAR,
TIMESTAMP,
Column,
ForeignKey,
Index,
MetaData,
String,
Table,
Text,
text,
)
from sqlalchemy.dialects.mysql import BIGINT, DECIMAL, INTEGER, TINYINT
from sqlalchemy.ext.compiler import compiles
import aurweb.config
db_backend = aurweb.config.get("database", "backend")
@compiles(TINYINT, "sqlite")
def compile_tinyint_sqlite(type_, compiler, **kw): # pragma: no cover
"""TINYINT is not supported on SQLite. Substitute it with INTEGER."""
return "INTEGER"
@compiles(BIGINT, "sqlite")
def compile_bigint_sqlite(type_, compiler, **kw): # pragma: no cover
"""
For SQLite's AUTOINCREMENT to work on BIGINT columns, we need to map BIGINT
to INTEGER. Aside from that, BIGINT is the same as INTEGER for SQLite.
See https://docs.sqlalchemy.org/en/13/dialects/sqlite.html#allowing-autoincrement-behavior-sqlalchemy-types-other-than-integer-integer
""" # noqa: E501
return "INTEGER"
metadata = MetaData()
# Define the Account Types for the AUR.
AccountTypes = Table(
"AccountTypes",
metadata,
Column("ID", TINYINT(unsigned=True), primary_key=True),
Column("AccountType", String(32), nullable=False, server_default=text("''")),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# User information for each user regardless of type.
Users = Table(
"Users",
metadata,
Column("ID", INTEGER(unsigned=True), primary_key=True),
Column(
"AccountTypeID",
ForeignKey("AccountTypes.ID", ondelete="NO ACTION"),
nullable=False,
server_default=text("1"),
),
Column(
"Suspended", TINYINT(unsigned=True), nullable=False, server_default=text("0")
),
Column("Username", String(32), nullable=False, unique=True),
Column("Email", String(254), nullable=False, unique=True),
Column("BackupEmail", String(254)),
Column(
"HideEmail", TINYINT(unsigned=True), nullable=False, server_default=text("0")
),
Column("Passwd", String(255), nullable=False),
Column("Salt", CHAR(32), nullable=False, server_default=text("''")),
Column("ResetKey", CHAR(32), nullable=False, server_default=text("''")),
Column("RealName", String(64), nullable=False, server_default=text("''")),
Column("LangPreference", String(6), nullable=False, server_default=text("'en'")),
Column("Timezone", String(32), nullable=False, server_default=text("'UTC'")),
Column("Homepage", Text),
Column("IRCNick", String(32), nullable=False, server_default=text("''")),
Column("PGPKey", String(40)),
Column(
"LastLogin", BIGINT(unsigned=True), nullable=False, server_default=text("0")
),
Column("LastLoginIPAddress", String(45)),
Column(
"LastSSHLogin", BIGINT(unsigned=True), nullable=False, server_default=text("0")
),
Column("LastSSHLoginIPAddress", String(45)),
Column(
"InactivityTS", BIGINT(unsigned=True), nullable=False, server_default=text("0")
),
Column(
"RegistrationTS",
TIMESTAMP,
nullable=False,
server_default=text("CURRENT_TIMESTAMP"),
),
Column("CommentNotify", TINYINT(1), nullable=False, server_default=text("1")),
Column("UpdateNotify", TINYINT(1), nullable=False, server_default=text("0")),
Column("OwnershipNotify", TINYINT(1), nullable=False, server_default=text("1")),
Column("SSOAccountID", String(255), nullable=True, unique=True),
Index("UsersAccountTypeID", "AccountTypeID"),
Column(
"HideDeletedComments",
TINYINT(unsigned=True),
nullable=False,
server_default=text("0"),
),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# SSH public keys used for the aurweb SSH/Git interface.
SSHPubKeys = Table(
"SSHPubKeys",
metadata,
Column("UserID", ForeignKey("Users.ID", ondelete="CASCADE"), nullable=False),
Column("Fingerprint", String(44), primary_key=True),
Column("PubKey", String(4096), nullable=False),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_bin",
)
# Track Users logging in/out of AUR web site.
Sessions = Table(
"Sessions",
metadata,
Column("UsersID", ForeignKey("Users.ID", ondelete="CASCADE"), nullable=False),
Column("SessionID", CHAR(32), nullable=False, unique=True),
Column("LastUpdateTS", BIGINT(unsigned=True), nullable=False),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_bin",
)
# Information on package bases
PackageBases = Table(
"PackageBases",
metadata,
Column("ID", INTEGER(unsigned=True), primary_key=True),
Column("Name", String(255), nullable=False, unique=True),
Column(
"NumVotes", INTEGER(unsigned=True), nullable=False, server_default=text("0")
),
Column(
"Popularity",
DECIMAL(10, 6, unsigned=True) if db_backend == "mysql" else String(17),
nullable=False,
server_default=text("0"),
),
Column(
"PopularityUpdated",
TIMESTAMP,
nullable=False,
server_default=text("'1970-01-01 00:00:01.000000'"),
),
Column("OutOfDateTS", BIGINT(unsigned=True)),
Column("FlaggerComment", Text, nullable=False),
Column("SubmittedTS", BIGINT(unsigned=True), nullable=False),
Column("ModifiedTS", BIGINT(unsigned=True), nullable=False),
Column(
"FlaggerUID", ForeignKey("Users.ID", ondelete="SET NULL")
), # who flagged the package out-of-date?
# deleting a user will cause packages to be orphaned, not deleted
Column(
"SubmitterUID", ForeignKey("Users.ID", ondelete="SET NULL")
), # who submitted it?
Column("MaintainerUID", ForeignKey("Users.ID", ondelete="SET NULL")), # User
Column("PackagerUID", ForeignKey("Users.ID", ondelete="SET NULL")), # Last packager
Index("BasesMaintainerUID", "MaintainerUID"),
Index("BasesNumVotes", "NumVotes"),
Index("BasesPackagerUID", "PackagerUID"),
Index("BasesSubmitterUID", "SubmitterUID"),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Keywords of package bases
PackageKeywords = Table(
"PackageKeywords",
metadata,
Column(
"PackageBaseID",
ForeignKey("PackageBases.ID", ondelete="CASCADE"),
primary_key=True,
nullable=True,
),
Column(
"Keyword",
String(255),
primary_key=True,
nullable=False,
server_default=text("''"),
),
Index("KeywordsPackageBaseID", "PackageBaseID"),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Information about the actual packages
Packages = Table(
"Packages",
metadata,
Column("ID", INTEGER(unsigned=True), primary_key=True),
Column(
"PackageBaseID",
ForeignKey("PackageBases.ID", ondelete="CASCADE"),
nullable=False,
),
Column("Name", String(255), nullable=False, unique=True),
Column("Version", String(255), nullable=False, server_default=text("''")),
Column("Description", String(255)),
Column("URL", String(8000)),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Information about licenses
Licenses = Table(
"Licenses",
metadata,
Column("ID", INTEGER(unsigned=True), primary_key=True),
Column("Name", String(255), nullable=False, unique=True),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Information about package-license-relations
PackageLicenses = Table(
"PackageLicenses",
metadata,
Column(
"PackageID",
ForeignKey("Packages.ID", ondelete="CASCADE"),
primary_key=True,
nullable=True,
),
Column(
"LicenseID",
ForeignKey("Licenses.ID", ondelete="CASCADE"),
primary_key=True,
nullable=True,
),
mysql_engine="InnoDB",
)
# Information about groups
Groups = Table(
"Groups",
metadata,
Column("ID", INTEGER(unsigned=True), primary_key=True),
Column("Name", String(255), nullable=False, unique=True),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Information about package-group-relations
PackageGroups = Table(
"PackageGroups",
metadata,
Column(
"PackageID",
ForeignKey("Packages.ID", ondelete="CASCADE"),
primary_key=True,
nullable=True,
),
Column(
"GroupID",
ForeignKey("Groups.ID", ondelete="CASCADE"),
primary_key=True,
nullable=True,
),
mysql_engine="InnoDB",
)
# Define the package dependency types
DependencyTypes = Table(
"DependencyTypes",
metadata,
Column("ID", TINYINT(unsigned=True), primary_key=True),
Column("Name", String(32), nullable=False, server_default=text("''")),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Track which dependencies a package has
PackageDepends = Table(
"PackageDepends",
metadata,
Column("PackageID", ForeignKey("Packages.ID", ondelete="CASCADE"), nullable=False),
Column(
"DepTypeID",
ForeignKey("DependencyTypes.ID", ondelete="NO ACTION"),
nullable=False,
),
Column("DepName", String(255), nullable=False),
Column("DepDesc", String(255)),
Column("DepCondition", String(255)),
Column("DepArch", String(255)),
Index("DependsDepName", "DepName"),
Index("DependsPackageID", "PackageID"),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Define the package relation types
RelationTypes = Table(
"RelationTypes",
metadata,
Column("ID", TINYINT(unsigned=True), primary_key=True),
Column("Name", String(32), nullable=False, server_default=text("''")),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Track which conflicts, provides and replaces a package has
PackageRelations = Table(
"PackageRelations",
metadata,
Column("PackageID", ForeignKey("Packages.ID", ondelete="CASCADE"), nullable=False),
Column(
"RelTypeID",
ForeignKey("RelationTypes.ID", ondelete="NO ACTION"),
nullable=False,
),
Column("RelName", String(255), nullable=False),
Column("RelCondition", String(255)),
Column("RelArch", String(255)),
Index("RelationsPackageID", "PackageID"),
Index("RelationsRelName", "RelName"),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Track which sources a package has
PackageSources = Table(
"PackageSources",
metadata,
Column("PackageID", ForeignKey("Packages.ID", ondelete="CASCADE"), nullable=False),
Column("Source", String(8000), nullable=False, server_default=text("'/dev/null'")),
Column("SourceArch", String(255)),
Index("SourcesPackageID", "PackageID"),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Track votes for packages
PackageVotes = Table(
"PackageVotes",
metadata,
Column("UsersID", ForeignKey("Users.ID", ondelete="CASCADE"), nullable=False),
Column(
"PackageBaseID",
ForeignKey("PackageBases.ID", ondelete="CASCADE"),
nullable=False,
),
Column("VoteTS", BIGINT(unsigned=True), nullable=False),
Index("VoteUsersIDPackageID", "UsersID", "PackageBaseID", unique=True),
Index("VotesPackageBaseID", "PackageBaseID"),
Index("VotesUsersID", "UsersID"),
mysql_engine="InnoDB",
)
# Record comments for packages
PackageComments = Table(
"PackageComments",
metadata,
Column("ID", BIGINT(unsigned=True), primary_key=True),
Column(
"PackageBaseID",
ForeignKey("PackageBases.ID", ondelete="CASCADE"),
nullable=False,
),
Column("UsersID", ForeignKey("Users.ID", ondelete="SET NULL")),
Column("Comments", Text, nullable=False),
Column("RenderedComment", Text, nullable=False),
Column(
"CommentTS", BIGINT(unsigned=True), nullable=False, server_default=text("0")
),
Column("EditedTS", BIGINT(unsigned=True)),
Column("EditedUsersID", ForeignKey("Users.ID", ondelete="SET NULL")),
Column("DelTS", BIGINT(unsigned=True)),
Column("DelUsersID", ForeignKey("Users.ID", ondelete="CASCADE")),
Column("PinnedTS", BIGINT(unsigned=True), nullable=False, server_default=text("0")),
Index("CommentsPackageBaseID", "PackageBaseID"),
Index("CommentsUsersID", "UsersID"),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Package base co-maintainers
PackageComaintainers = Table(
"PackageComaintainers",
metadata,
Column("UsersID", ForeignKey("Users.ID", ondelete="CASCADE"), nullable=False),
Column(
"PackageBaseID",
ForeignKey("PackageBases.ID", ondelete="CASCADE"),
nullable=False,
),
Column("Priority", INTEGER(unsigned=True), nullable=False),
Index("ComaintainersPackageBaseID", "PackageBaseID"),
Index("ComaintainersUsersID", "UsersID"),
mysql_engine="InnoDB",
)
# Package base notifications
PackageNotifications = Table(
"PackageNotifications",
metadata,
Column(
"PackageBaseID",
ForeignKey("PackageBases.ID", ondelete="CASCADE"),
nullable=False,
),
Column("UserID", ForeignKey("Users.ID", ondelete="CASCADE"), nullable=False),
Index("NotifyUserIDPkgID", "UserID", "PackageBaseID", unique=True),
mysql_engine="InnoDB",
)
# Package name blacklist
PackageBlacklist = Table(
"PackageBlacklist",
metadata,
Column("ID", INTEGER(unsigned=True), primary_key=True),
Column("Name", String(64), nullable=False, unique=True),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Providers in the official repositories
OfficialProviders = Table(
"OfficialProviders",
metadata,
Column("ID", INTEGER(unsigned=True), primary_key=True),
Column("Name", String(64), nullable=False),
Column("Repo", String(64), nullable=False),
Column("Provides", String(64), nullable=False),
Index("ProviderNameProvides", "Name", "Provides", unique=True),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_bin",
)
# Define package request types
RequestTypes = Table(
"RequestTypes",
metadata,
Column("ID", TINYINT(unsigned=True), primary_key=True),
Column("Name", String(32), nullable=False, server_default=text("''")),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Package requests
PackageRequests = Table(
"PackageRequests",
metadata,
Column("ID", BIGINT(unsigned=True), primary_key=True),
Column(
"ReqTypeID", ForeignKey("RequestTypes.ID", ondelete="NO ACTION"), nullable=False
),
Column("PackageBaseID", ForeignKey("PackageBases.ID", ondelete="SET NULL")),
Column("PackageBaseName", String(255), nullable=False),
Column("MergeBaseName", String(255)),
Column("UsersID", ForeignKey("Users.ID", ondelete="SET NULL")),
Column("Comments", Text, nullable=False),
Column("ClosureComment", Text, nullable=False),
Column(
"RequestTS", BIGINT(unsigned=True), nullable=False, server_default=text("0")
),
Column("ClosedTS", BIGINT(unsigned=True)),
Column("ClosedUID", ForeignKey("Users.ID", ondelete="SET NULL")),
Column("Status", TINYINT(unsigned=True), nullable=False, server_default=text("0")),
Index("RequestsPackageBaseID", "PackageBaseID"),
Index("RequestsUsersID", "UsersID"),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Vote information
TU_VoteInfo = Table(
"TU_VoteInfo",
metadata,
Column("ID", INTEGER(unsigned=True), primary_key=True),
Column("Agenda", Text, nullable=False),
Column("User", String(32), nullable=False),
Column("Submitted", BIGINT(unsigned=True), nullable=False),
Column("End", BIGINT(unsigned=True), nullable=False),
Column(
"Quorum",
DECIMAL(2, 2, unsigned=True) if db_backend == "mysql" else String(5),
nullable=False,
),
Column("SubmitterID", ForeignKey("Users.ID", ondelete="CASCADE"), nullable=False),
Column("Yes", INTEGER(unsigned=True), nullable=False, server_default=text("'0'")),
Column("No", INTEGER(unsigned=True), nullable=False, server_default=text("'0'")),
Column(
"Abstain", INTEGER(unsigned=True), nullable=False, server_default=text("'0'")
),
Column(
"ActiveTUs", INTEGER(unsigned=True), nullable=False, server_default=text("'0'")
),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Individual vote records
TU_Votes = Table(
"TU_Votes",
metadata,
Column("VoteID", ForeignKey("TU_VoteInfo.ID", ondelete="CASCADE"), nullable=False),
Column("UserID", ForeignKey("Users.ID", ondelete="CASCADE"), nullable=False),
mysql_engine="InnoDB",
)
# Malicious user banning
Bans = Table(
"Bans",
metadata,
Column("IPAddress", String(45), primary_key=True),
Column("BanTS", TIMESTAMP, nullable=False),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Terms and Conditions
Terms = Table(
"Terms",
metadata,
Column("ID", INTEGER(unsigned=True), primary_key=True),
Column("Description", String(255), nullable=False),
Column("URL", String(8000), nullable=False),
Column(
"Revision", INTEGER(unsigned=True), nullable=False, server_default=text("1")
),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)
# Terms and Conditions accepted by users
AcceptedTerms = Table(
"AcceptedTerms",
metadata,
Column("UsersID", ForeignKey("Users.ID", ondelete="CASCADE"), nullable=False),
Column("TermsID", ForeignKey("Terms.ID", ondelete="CASCADE"), nullable=False),
Column(
"Revision", INTEGER(unsigned=True), nullable=False, server_default=text("0")
),
mysql_engine="InnoDB",
)
# Rate limits for API
ApiRateLimit = Table(
"ApiRateLimit",
metadata,
Column("IP", String(45), primary_key=True, unique=True, default=str()),
Column("Requests", INTEGER(11), nullable=False),
Column("WindowStart", BIGINT(20), nullable=False),
Index("ApiRateLimitWindowStart", "WindowStart"),
mysql_engine="InnoDB",
mysql_charset="utf8mb4",
mysql_collate="utf8mb4_general_ci",
)