feat(postcard): profile country, country_from, per-user card number uniqueness, public page overhaul - Profile: add country field for default origin country - Postcard: add country_from field, auto-fill from profile.country on create - card_number uniqueness: change from global to per-user (profile_id + card_number) - Public pages: remove card_number display, only show cards with image_front - Public pages: show country as from→to format - Index page: no time display, add register button, fix lightbox with prev/next - Infinite scroll: add /api/public/cards endpoints and IntersectionObserver - Internal pages: update postcards list/detail for country_from display - i18n: add profiles.country_ph, pc_form.country_from, pc_detail.route

This commit is contained in:
2026-07-07 12:32:24 +08:00
parent bd664d23be
commit a071b3dde4
12 changed files with 414 additions and 90 deletions

View File

@@ -1,6 +1,6 @@
from datetime import datetime, timezone
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import relationship, validates
from app.database import Base
@@ -61,6 +61,7 @@ class Profile(Base):
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
nickname = Column(String(64), nullable=False)
country = Column(String(2), nullable=True)
showcase_enabled = Column(default=False, nullable=False)
showcase_visible = Column(String(16), nullable=False, default="both")
notes = Column(Text, nullable=False, default="")
@@ -72,14 +73,18 @@ class Profile(Base):
class Postcard(Base):
__tablename__ = "postcards"
__table_args__ = (
UniqueConstraint("profile_id", "card_number", name="uq_postcards_profile_card_number"),
)
id = Column(Integer, primary_key=True, index=True)
profile_id = Column(Integer, ForeignKey("profiles.id", ondelete="CASCADE"), nullable=False)
card_number = Column(String(64), nullable=False, unique=True)
card_number = Column(String(64), nullable=False)
status = Column(String(16), nullable=False)
# sent & delivered
recipient_name = Column(String(64))
country = Column(String(64))
country_from = Column(String(2))
send_time = Column(DateTime)
arrival_time = Column(DateTime)
# received
@@ -100,6 +105,10 @@ class Postcard(Base):
def _upper_country(self, key, value):
return value.strip().upper() if value else None
@validates("country_from")
def _upper_country_from(self, key, value):
return value.strip().upper() if value else None
class ApiKey(Base):
__tablename__ = "api_keys"