Introduced new model: Plan. This differs from the legacy subscriptions because its based on time in months and supports multiple protocols at once in a list enum format

This commit is contained in:
SimplifiedPrivacy 2026-10-03 14:47:38 -04:00
parent f2922b02a0
commit 9627b1861d
7 changed files with 70 additions and 6 deletions

View file

@ -6,7 +6,7 @@ import os
@dataclass(frozen=True)
class Constants:
DB_VERSION_THIS_APP_WANTS = 2
DB_VERSION_THIS_APP_WANTS = 3
# Fallback for development (running outside AppImage)
fallback_non_appimage = os.path.dirname(os.path.abspath(__file__))

View file

@ -16,6 +16,7 @@ from core.errors.logger import logger
from core.models.orm_models.Location import Location
from core.models.orm_models.Operator import Operator
from core.models.orm_models.Dependency import Dependency
from core.models.orm_models.Plan import Plan
from core.Constants import Constants
from core.controllers.ApplicationController import ApplicationController
@ -35,7 +36,8 @@ ORM_TABLES = {
"locations": Location,
"operators": Operator,
"application_versions": ApplicationVersion,
"dependencies": Dependency
"dependencies": Dependency,
"plans": Plan,
}
LEGACY_SQL_FUNCT_DICT = {

View file

@ -67,7 +67,8 @@ def migrate_sql() -> DatabaseOperation:
from core.models.orm_models.CachedSync import CachedSync
from core.models.orm_models.EncryptedProxy import EncryptedProxy
from core.models.orm_models.Dependency import Dependency
MODELS = [Location, Operator, CachedSync, EncryptedProxy, Dependency]
from core.models.orm_models.Plan import Plan
MODELS = [Location, Operator, CachedSync, EncryptedProxy, Dependency, Plan]
logger.info(f"[MIGRATION] Tables loaded")
except:
logger.error(f"[MIGRATION] Could not load models. Critical failure.")

View file

@ -146,9 +146,11 @@ def create_ALL_tables():
from core.models.orm_models.Operator import Operator
from core.models.orm_models.CachedSync import CachedSync
from core.models.orm_models.EncryptedProxy import EncryptedProxy
from core.models.SubscriptionPlan import SubscriptionPlan
from core.models.orm_models.Plan import Plan
from core.models.orm_models.ApplicationVersion import ApplicationVersion
from core.models.orm_models.Dependency import Dependency
# legacy:
from core.models.SubscriptionPlan import SubscriptionPlan
try:
BaseModel.metadata.create_all(engine, checkfirst=True)

View file

@ -21,5 +21,13 @@ class CachedSync(Base):
client_version: Mapped[Optional[int]] = mapped_column(Integer, nullable=False, default=0)
operators: Mapped[Optional[int]] = mapped_column(Integer, nullable=False, default=0)
locations: Mapped[Optional[int]] = mapped_column(Integer, nullable=False, default=0)
subscriptions: Mapped[Optional[int]] = mapped_column(Integer, nullable=False, default=0)
dependencies: Mapped[Optional[int]] = mapped_column(Integer, nullable=False, default=0)
# legacy:
subscriptions: Mapped[Optional[int]] = mapped_column(Integer, nullable=False, default=0)
# v2 refactor:
plans: Mapped[Optional[int]] = mapped_column(Integer, nullable=False, default=0)

View file

@ -0,0 +1,50 @@
from core.models.enum_models import ProtocolChoice
from core.models.orm_models.Base import BaseModel
from core.Constants import Constants
from core.errors.logger import logger
import enum
from sqlalchemy.orm import Mapped
from sqlalchemy import Column, Integer, String, Boolean, UniqueConstraint, JSON, TypeDecorator, Numeric
from sqlalchemy.orm import declarative_base
class EnumList(TypeDecorator):
"""Stores a list of ProtocolChoice enums as a JSON list of strings."""
impl = JSON
def process_bind_param(self, value, dialect):
if value is not None:
# Create a set of all valid values
valid_values = {item.value for item in ProtocolChoice}
filtered = []
for item in value:
# 1. Get the raw value regardless of if it's an Enum or String
val = item.value if isinstance(item, enum.Enum) else item
# 2. Only keep it if it exists in our valid_values set
if val in valid_values:
filtered.append(val)
else:
logger.error(f"Dropped an invalid item: {val}")
pass
return filtered
return None
def process_result_value(self, value, dialect):
# Convert strings from DB back into enum members
if value is not None:
return [ProtocolChoice(item) for item in value]
return None
class Plan(BaseModel):
__tablename__ = 'plans'
id = Column(Integer, primary_key=True)
months = Column(Integer, unique=False, nullable=False)
supported_protocols = Column(EnumList, nullable=False)
cost = Column(Numeric(precision=10, scale=2), nullable=False)

View file

@ -10,7 +10,8 @@ def switch_endpoint_domain(domain: str) -> dict:
"subscriptions": f"https://{domain}/api/v1/subscription-plans",
"applications": f"https://{domain}/api/v1/platforms/linux-x86_64/applications",
"application_versions": f"https://{domain}/api/v1/platforms/linux-x86_64/application-versions",
"dependencies": f"https://{domain}/api/v1/dependencies"
"dependencies": f"https://{domain}/api/v1/dependencies",
"plans": f"https://{domain}/api/v2/time_based_plans"
}