Drop the last legacy widget-system and shared-token auth scaffolding
Firmware build check / build-check (push) Successful in 5m37s
Build and release firmware / build-and-release (push) Successful in 5m36s
Build and push server image / test (push) Successful in 1m37s
Build and push server image / build-and-push (push) Successful in 4m18s
Build and push server image / deploy (push) Failing after 1m20s
Firmware build check / build-check (push) Successful in 5m37s
Build and release firmware / build-and-release (push) Successful in 5m36s
Build and push server image / test (push) Successful in 1m37s
Build and push server image / build-and-push (push) Successful in 4m18s
Build and push server image / deploy (push) Failing after 1m20s
Server: migration 41 drops the pre-widget-system Frame columns (mode/album_id/current_asset_id/queue/calendar_*/whiteboard_*, etc) docs/widgets.md flagged as the deliberately-deferred Phase 6 cleanup, with a raw-SQL backfill safety net for any frame that still somehow lacks a Widget. Also drops legacy_token_enabled and the shared MANAGEMENT_TOKEN fallback it gated in require_device/require_browser -- the per-frame manage_token/device_token flow (and the /m/ page) fully supersede it now; MANAGEMENT_TOKEN's only remaining role is the optional pre-setup claim gate. Confirmed with the maintainer that the deployed frame is already off the shared token before removing the server-side fallback. Firmware: the captive portal's "Access Token" field and its NVS/ build_url plumbing only ever mattered for pointing new firmware at an old pre-multi-frame server -- gone along with the server-side fallback it fed. Version bump to publish the change.
This commit is contained in:
+3
-125
@@ -117,14 +117,10 @@ class Frame(Base):
|
||||
__tablename__ = "frames"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
# 12 lowercase hex chars of the device's full STA MAC. NULL only for
|
||||
# the migrated legacy frame until its device first reports an id.
|
||||
# 12 lowercase hex chars of the device's full STA MAC. NULL until its
|
||||
# device first reports an id.
|
||||
device_id: Mapped[str | None] = mapped_column(String, unique=True, nullable=True)
|
||||
name: Mapped[str] = mapped_column(String, default="")
|
||||
# Renderer dispatch seam -- "photos", "calendar", or "whiteboard"
|
||||
# (see routers/device.py RENDERERS/ADVANCE_RENDERERS/BACK_RENDERERS
|
||||
# and routers/common.py FRAME_MODES).
|
||||
mode: Mapped[str] = mapped_column(String, default="photos")
|
||||
# Whose Immich library this frame pulls from; NULL = unclaimed.
|
||||
owner_user_id: Mapped[int | None] = mapped_column(
|
||||
ForeignKey("users.id", ondelete="SET NULL"), nullable=True
|
||||
@@ -138,11 +134,6 @@ class Frame(Base):
|
||||
# pushing it in /frame/config responses.
|
||||
device_token_ack: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
manage_token: Mapped[str] = mapped_column(String, unique=True)
|
||||
# Migration window: this frame also accepts the legacy shared
|
||||
# MANAGEMENT_TOKEN (and no-id requests resolve to it). Only ever the
|
||||
# migrated frame #1; cleared from /admin once the device is on
|
||||
# per-frame auth.
|
||||
legacy_token_enabled: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
claimed_at: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
created_at: Mapped[float] = mapped_column(Float, default=time.time)
|
||||
|
||||
@@ -153,19 +144,13 @@ class Frame(Base):
|
||||
immich_url: Mapped[str] = mapped_column(String, default="")
|
||||
immich_api_key: Mapped[str] = mapped_column(String, default="")
|
||||
|
||||
# -- settings (attribute names match the old FrameConfig fields) --
|
||||
album_id: Mapped[str] = mapped_column(String, default="")
|
||||
order: Mapped[str] = mapped_column("photo_order", String, default="sequential")
|
||||
# -- settings --
|
||||
refresh_interval_s: Mapped[int] = mapped_column(Integer, default=3600)
|
||||
quiet_hours_enabled: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
quiet_hours_start: Mapped[str] = mapped_column(String, default="22:00")
|
||||
quiet_hours_end: Mapped[str] = mapped_column(String, default="07:00")
|
||||
timezone: Mapped[str] = mapped_column(String, default="UTC")
|
||||
# How a photo's aspect ratio is reconciled with the panel's -- see
|
||||
# image_pipeline.DISPLAY_MODES.
|
||||
display_mode: Mapped[str] = mapped_column(String, default="crop_faces")
|
||||
orientation: Mapped[str] = mapped_column(String, default="landscape")
|
||||
queue_target_len: Mapped[int] = mapped_column(Integer, default=20)
|
||||
# Advanced configuration: [[r,g,b], ...] x6 (black/white/yellow/red/
|
||||
# blue/green, matching image_pipeline.PANEL_CODES order) overriding
|
||||
# DEFAULT_PALETTE_RGB for this frame's actual panel. NULL = use the
|
||||
@@ -192,113 +177,6 @@ class Frame(Base):
|
||||
# Widgets rendered in classic (PIL) style ignore this entirely.
|
||||
theme: Mapped[str] = mapped_column(String, default="classic")
|
||||
|
||||
# -- calendar mode (see calendar_feed.py, calendar_render.py,
|
||||
# routers/device.py's RENDERERS["calendar"]) --
|
||||
calendar_view: Mapped[str] = mapped_column(String, default="agenda") # "agenda" | "week" | "month"
|
||||
# 0=Monday..6=Sunday (matches date.weekday()/calendar.Calendar) --
|
||||
# which day week/month views start their grid on.
|
||||
calendar_week_start: Mapped[int] = mapped_column(Integer, default=0)
|
||||
# Agenda view only; reuses this frame's existing photos-mode album/
|
||||
# queue, not a separate photo setup.
|
||||
calendar_photo_inlay: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
# How many periods (unit depends on calendar_view: days/weeks/months)
|
||||
# NEXT/BACK have browsed from "today". Reset to 0 by the next normal
|
||||
# (non-button) /frame/image request, and whenever calendar_view
|
||||
# itself changes -- a stale offset means something different in a
|
||||
# different view's units.
|
||||
calendar_browse_offset: Mapped[int] = mapped_column(Integer, default=0)
|
||||
# Throttled merge-fetch cache (see routers/common.py's
|
||||
# get_or_refresh_calendar_events) -- same shape as the
|
||||
# firmware_update_checked_at/firmware_gitea_latest_version pattern
|
||||
# below. One shared cache for every included user's merged events,
|
||||
# not per-user.
|
||||
calendar_checked_at: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
calendar_cached_events: Mapped[list | None] = mapped_column(JSON, nullable=True, default=None)
|
||||
# "" when the last merge-fetch fully succeeded, else e.g. "1 of 2
|
||||
# calendars unavailable" -- never names which user's feed failed, a
|
||||
# shared household display shouldn't call out a specific person's
|
||||
# outage to everyone who looks at it.
|
||||
calendar_fetch_summary: Mapped[str] = mapped_column(String, default="")
|
||||
|
||||
# Optional weather strip, agenda/today & tomorrow/week views only --
|
||||
# never month, there's no room (see calendar_render.py's _BUILDERS).
|
||||
# Off by default.
|
||||
calendar_weather_enabled: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
calendar_weather_units: Mapped[str] = mapped_column(String, default="fahrenheit") # "fahrenheit" | "celsius"
|
||||
# [{"label", "latitude", "longitude"}, ...] -- each geocoded once via
|
||||
# weather.geocode_city() when added from the Calendar tab.
|
||||
calendar_weather_cities: Mapped[list | None] = mapped_column(JSON, nullable=True, default=None)
|
||||
# Throttled per-city forecast cache (see routers/common.py's
|
||||
# get_or_refresh_weather) -- same shape idiom as
|
||||
# calendar_checked_at/calendar_cached_events above.
|
||||
# [{"label", "days": {"YYYY-MM-DD": {"code","high","low"}}}, ...]
|
||||
calendar_weather_checked_at: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
calendar_weather_cached: Mapped[list | None] = mapped_column(JSON, nullable=True, default=None)
|
||||
|
||||
# Week view: how many days to show (2-10, default 7 -- the original
|
||||
# fixed behavior) and whether they're laid out as side-by-side
|
||||
# columns or stacked bands (see calendar_render.py's _build_week).
|
||||
calendar_week_days: Mapped[int] = mapped_column(Integer, default=7)
|
||||
calendar_week_layout: Mapped[str] = mapped_column(String, default="horizontal") # "horizontal" | "vertical"
|
||||
# Only used when calendar_week_days != 7 -- calendar_week_start's
|
||||
# fixed-weekday anchor ("start on the most recent Monday") stops
|
||||
# making sense once the view isn't a literal calendar week, so a
|
||||
# non-7-day view instead starts this many days from today (0 =
|
||||
# starts today, negative = starts in the past, positive = starts in
|
||||
# the future). Ignored (calendar_week_start governs instead) at the
|
||||
# default 7 days, so this has no effect until someone actually
|
||||
# changes the day count.
|
||||
calendar_week_start_offset: Mapped[int] = mapped_column(Integer, default=0)
|
||||
|
||||
# Optional task list, week view only -- takes the space of one day
|
||||
# slot rather than adding an extra one (see calendar_render.py's
|
||||
# _draw_tasks). CalDAV only (a task list is a VTODO collection, not
|
||||
# something a plain ICS subscription meaningfully has); source is
|
||||
# one specific linked user's own CalDAV calendar, same
|
||||
# owner-controls-their-own-data permission split as FrameCalendar.
|
||||
# calendar_tasks_user_id
|
||||
# SET NULL on the user's deletion clears the source rather than
|
||||
# leaving a dangling reference (checked_at isn't reset by that, but
|
||||
# the next refresh attempt finds no source and just returns []).
|
||||
calendar_tasks_enabled: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
calendar_tasks_user_id: Mapped[int | None] = mapped_column(
|
||||
ForeignKey("users.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
calendar_tasks_calendar_key: Mapped[str | None] = mapped_column(String, nullable=True)
|
||||
calendar_tasks_checked_at: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
# [{"summary", "due" (ISO date/datetime string or None)}, ...],
|
||||
# already filtered to outstanding (not-completed) tasks and sorted
|
||||
# by due date -- see caldav_client.fetch_tasks.
|
||||
calendar_tasks_cached: Mapped[list | None] = mapped_column(JSON, nullable=True, default=None)
|
||||
|
||||
# -- whiteboard mode (see webdav_client.py, whiteboard.py,
|
||||
# routers/device.py's RENDERERS["whiteboard"]) -- a frame-wide
|
||||
# setting like calendar mode's own frame_calendars source, not
|
||||
# personal data, but still owner-gated the same way: only
|
||||
# whiteboard_user_id may point the frame at their own account, since
|
||||
# it's their credentials being used to fetch it. --
|
||||
whiteboard_user_id: Mapped[int | None] = mapped_column(
|
||||
ForeignKey("users.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
# The specific .whiteboard file's WebDAV URL -- pasted directly, same
|
||||
# idiom as calendar_ics_url, not discovered/browsed (unlike CalDAV's
|
||||
# account-has-several-calendars case, a WebDAV account doesn't need
|
||||
# a picker step here since the user already knows which one file).
|
||||
whiteboard_url: Mapped[str] = mapped_column(String, default="")
|
||||
whiteboard_checked_at: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
# Cached rendered PNG bytes (see whiteboard.fetch_and_render) --
|
||||
# BLOB rather than the JSON columns the rest of this cache-pattern
|
||||
# family uses, since this is binary image data, not JSON-shaped.
|
||||
whiteboard_cached_image: Mapped[bytes | None] = mapped_column(LargeBinary, nullable=True)
|
||||
|
||||
# -- state --
|
||||
current_asset_id: Mapped[str] = mapped_column(String, default="")
|
||||
current_asset_set_at: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
queue: Mapped[list] = mapped_column(MutableList.as_mutable(JSON), default=list)
|
||||
queue_cursor: Mapped[int] = mapped_column(Integer, default=0)
|
||||
history: Mapped[list] = mapped_column(MutableList.as_mutable(JSON), default=list)
|
||||
excluded_asset_ids: Mapped[list] = mapped_column(MutableList.as_mutable(JSON), default=list)
|
||||
|
||||
# -- telemetry --
|
||||
battery_percent: Mapped[int] = mapped_column(Integer, default=-1)
|
||||
battery_as_of: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
|
||||
Reference in New Issue
Block a user