Skip to content
CelPlume
↑↓select·Enteropen·EscclosePowered by Pagefind
ZH
On this page

Changelog

Full version history of SDNUChronoSync.

All versions are archived here; the homepage only displays the last three months.

v3.6.5 (2026-08-16) - Unified academic query module: empty classrooms / exams / grades

Section titled “v3.6.5 (2026-08-16) - Unified academic query module: empty classrooms / exams / grades”

Unified academic query entry

  • Reworked “empty classroom lookup” into a unified “Academic Query” entry (nav “Academic Query”, URL /dashboard/query) with three tabs: empty classrooms / exams / grades; backend consolidated onto /api/query session and routes, removing the old /api/classroom
  • Empty classroom query: filter by semester / campus / building / venue category / capacity / venue name / weeks / weekday / periods, with export support
  • Tabs span the full page width (consistent on landscape / portrait); welcome page shown by default after login; URL ?tab=grade deep-links to a tab

Exam query and import

  • Exam query: filter by exam name / time / college / course, showing date, time, location, seat, offering college
  • One-click import to schedule: import into an existing schedule or create a new one (title “Course name (Exam)”, auto-parsed start/end times and location)

Grade query and profile display

  • Grade query: auto-selects the current semester and shows results directly; shows both all-semester GPA and current-semester GPA, with the all-semester GPA counted as the official one
  • Grade filters: all / pass / fail / makeup / retake-pass (based on the academic system’s exam-nature field)
  • “Show in profile” checkbox persists a semester grade snapshot to the profile “Grades” tab, viewable without re-login

Academic login and session

  • Academic login is now a two-step dialog (WebVPN → academic account), captcha refreshes on click
  • Removed local session timeout: every query live-checks the academic system; upstream login-expiry pages (including unified-auth security-wall pages) now show a unified “academic login expired” prompt and re-login dialog
  • Exams / grades default to the current semester, falling back to the just-ended spring semester during summer break

Academic login gate and queue

  • Login serialization: global 30s login gate + queue cap of 10 (credentials not queued in gate mode; the frontend submits real login only when it’s the user’s turn); 3 login attempts within 30 minutes for the same account lock it for 30 minutes
  • Global circuit breaker: evidence of the school’s security-wall (3 times / 10 minutes) pauses login for 1-5 minutes to avoid triggering school CAS risk control
  • Queue UX: login dialog shows “position / estimated wait”, auto-submits when it’s the user’s turn; timed-out queue items auto-re-enqueue (max 2 times); closing the dialog cancels the queue
  • Session keep-alive: successful academic sessions are lightly probed every 25 minutes (multi-worker election via Redis distributed lock); security-wall detection marks the session expired and reports a breaker event
  • Encrypted cookie storage: academic cookie jars are Fernet-encrypted into the database and Redis hot cache (jwxt:session:*); keys live only in server files / environment variables
  • Redis deployment: docker-compose adds a redis:7-alpine service (isolated internal network, health checks); app and Redis share gate / queue / breaker / cache state

Concurrency and security fixes

  • Lock ownership: login gate and keep-alive distributed locks carry random owner tokens; release compares tokens (compare-and-delete) so stale holders cannot delete a new holder’s lock
  • Atomicity: queueing (enqueue / peek / head-consume), breaker counts, session cache projections, and keep-alive scanning claims all moved to Redis Lua atomic scripts; an in-process fallback provides an equivalent single-lock implementation
  • Redis fail-closed: startup refused when REDIS_URL is configured but unreachable; runtime gate failures return 503 for login
  • Key security: Fernet key file created atomically (O_EXCL + 0600); encryption failures error out instead of writing plaintext cookies; key file excluded from the Docker build context and image
  • Session serialization: per-user mutex for academic operations (login / captcha / query / keep-alive probe); keep-alive scans use short per-user transactions
  • Version protection: session persistence and invalidation use updated_at version CAS in a single transaction, so stale keep-alive/probes cannot overwrite a fresh login
  • Database connection release: query / login / keep-alive routes close and return DB connections during upstream HTTP; persistence/invalidation use short transactions
  • Import session hardening: schedule import sessions moved to shared encrypted storage, bound to the creating user, atomically claimed; ZFW import is a single transaction
  • Auth hardening: legacy password-hash upgrades use version CAS; token_version increments atomically in SQL; production SECRET_KEY requires ≥32 chars and rejects placeholders; email uniqueness moved to a case-insensitive unique index; captcha emails get per-IP hourly quotas
  • Idempotent writes: duplicate shared-schedule imports return the original schedule (import ledger); ICS imports update idempotently by source_uid; batch/smart scheduling support Idempotency-Key
  • Team concurrency: batch deletion / member management use team row locks with stable ordering to avoid reverse lock-ring deadlocks; scheduling re-checks conflicts inside the row lock; preview endpoints are read-only; team transfer carries creator CAS
  • Public-share minimization: public share responses strip student IDs / class / grade / college; share permission enums reject unknown values with 422
  • Frontend flow governance: login / queue flows carry generation markers so stale polling/login responses are not written back; “remember password” cache gets a 7-day TTL and generation guard; 401 handling compares the token at request time

Site config and stability fixes

  • Added a “default semester start date” to site settings: admins set it under Settings → Site Config (saved to config.toml); it pre-fills the start date when users create schedules (also for academic / ICS import-created schedules), falling back to the creation day when unset
  • Fixed: academic login queue overflow (10 people) now correctly returns 429 with a “retry later” message, previously returning 500 due to a route variable shadowing fastapi.status; added route-level regression tests

Data migration and deployment

  • Added 5 Alembic revisions (current head d3e5f7a9b1c3): email unique index, email_send_logs, schedule_share_imports, events.source_uid, batch/scheduling idempotency_key
  • PostgreSQL deployments must run alembic upgrade head before startup; production direct academic endpoints refuse plaintext HTTP (unless explicitly JWXT_ALLOW_PLAIN_HTTP=1)

v3.6.4 (2026-08-13) - Full dark mode refactor and frontend design system

Section titled “v3.6.4 (2026-08-13) - Full dark mode refactor and frontend design system”

Semantic Token System

  • Established semantic CSS-variable tokens: --bg-page/--bg-card/--bg-muted/--border/--text/--accent etc., defined in BaseLayout.astro, with :root for light and html.dark for dark auto-switching; added layer tokens --layer-dropdown(1200)/popover(1250)/modal(1400)/toast(1500)
  • Tailwind darkMode: 'class'; neutral unifies scattered gray/slate, primary absorbs sky, removed secondary fuchsia palette
  • Six landing-page showcase mocks switched to consuming tokens (var(--bg-card) etc.), no more per-component hardcoded hex colors

Unified Components (consolidating scattered implementations)

  • Added InfoBox: info/warning/danger variants, migrated TransferTeamModal / DissolveTeamModal / TemporaryTeamDrawer / ScheduleImporter etc.
  • Added TabBar: supports stretch for equal-width buttons, sliding indicator capsule with smooth switching (based on AinOfficialWiki Tabs pattern), migrated SystemSettings / TeamEditorModal / ScheduleAdjuster
  • PickerPopover panel and CodeEditor dark overrides (scoped :global(html.dark) fails in Vue SFC → moved to non-scoped <style>)

Tab System Conventions

  • Two tab types: page option switching (profile / system settings / team management, active dark:bg-neutral-700 + sliding indicator, no focus ring); selector switching (select all / clear, smart scheduling, container dark:bg-neutral-900 recessed track + active dark:bg-neutral-700 + light text)

Full dark-mode coverage for all components

  • Schedules (week/day/calendar/import/export/share/empty classroom/adjustment), teams (view/edit/heatmap/temporary/batch add/smart scheduling), admin (user/team/system settings), auth/landing/navigation/showcase - all supplemented with dark: variants

Interaction & Backend Fixes

  • Fixed nested modal (dissolve team) closing immediately on click: child modal toggle uses parent Dialog :static + handleClose guard
  • Fixed main button hover:bg-primary-50 white text invisibility and illegal opacity /300
  • Fixed preset avatar upload 400: rasterize SVG to PNG before upload (backend rejects SVG + avoids XSS)
  • Fixed availability 422: team/temporary availability route week limit 30→53 (frontend sends calendar weeks)
  • Fixed theme icon hydration mismatch: dual-render sun/moon with dark:block/hidden toggle

Avatars & Docs

  • 17 DiceBear preset accent variants consolidated to primary/neutral single-anchor tone
  • Added Frontend Design Guide (700+ lines): tokens, palette, unified components, two-tab conventions, component inventory, lessons learned, verification methodology, commit conventions; established cross-references in AGENTS.md / README

Verification

  • Frontend lint, type-check, and production build pass (14 pages)
  • Headless browser per-page dark audit: my-teams / team-view / user-management / team-management / system-settings etc. - all dark, 0 residual light
  • Real-device tests: TabBar sliding indicator aligns with active tab; dissolving modal inner-click does not close it
Section titled “v3.6.3 (2026-08-09) - Brand & link migration: docs centralization, Footer revamp, and CelPlume unification”
Section titled “v3.6.3 (2026-08-09) - Brand & link migration: docs centralization, Footer revamp, and CelPlume unification”

Documentation

In-site link migration

  • Tutorial URLs in the README, full changelog, and llms.txt/llms-full.txt are moved from hs.cnies.org to in-site pages: Nanny User Tutorial, Tutorial navigation, and Changelog. Frontend navigation components (Navigation, MobileDrawer, landing page) and their tutorial entry constants point to the same targets, with section anchors adjusted to match the new site structure (“Part 2 My Schedule”, “3-Import schedule”, “5-Holiday adjustments”, “Part 3 Team collaboration”).
  • The external project homepage link (hevspecu.hxcn.space) is replaced by the in-site index page.
  • Landing page navigation is trimmed to About / Tutorials / Meet Schedule, dropping the Changelog and Project homepage entries. The hero section drops the “Features” and “Tutorials” buttons, leaving only Sign up / Log in.

Footer revamp

  • Brand name updated to CelPlume, linking to the project homepage.
  • Removed the “Powered by Astro and Vue.js” line.
  • Added a Meet Schedule entry (https://meetschedule.top) with a calendar icon.
  • The “About” link now points to the in-site index page; the “Project homepage” entry is gone, replaced with on-site Terms of Service and Privacy Policy links.

Terms compliance

  • Login and registration forms now require checking “I have read and agree to the Terms of Service and Privacy Policy” (linking to the in-site legal pages). Submission is blocked until the box is checked.

Verification

  • Frontend lint, type-check, and production build all pass; every in-site page and section anchor was manually verified accessible.

v3.6.2 (2026-08-04) - Frontend bootstrap regression and avatar CORS cache fixes

Section titled “v3.6.2 (2026-08-04) - Frontend bootstrap regression and avatar CORS cache fixes”

Post-login page load

  • Fixed a regression introduced in v3.6.1 that left the post-login page stuck on “Loading…”. Astro was emitting <script define:vars> as an unpacked classic inline script, so its raw import statement tripped the browser’s “Cannot use import statement outside a module” error and initAuth never ran. The fix exposes the build-time changelog version via an inline variable script and loads the bootstrap as a proper type="module" bundle.
  • Avatar cross-origin cache requests now omit credentials (credentials: 'omit'). Alist returns Access-Control-Allow-Origin: *, which the browser rejects for credentialed requests. This was the source of the persistent CORS errors in the console.

Changelog modal and “new version” badge

  • The changelog modal was rebuilt to match the project’s component conventions: a unified ModalTitleCard header (icon + title), a narrower max-w-3xl panel with rounded-xl corners and a stroke, a bottom action bar with a rounded “Close” button, and removal of the redundant subtitle and extra absolute-positioned close button.
  • Fixed the “new version” badge and auto-popup, which never actually fired. The bootstrap script and each Astro island component each created their own Pinia instance (via src/pages/_app.ts), so the hasNewVersion flag set by the bootstrap script was never visible to the modal. The fix has the bootstrap broadcast a chronosync:changelog-new window event that the modal listens for and reacts to.

Login and load performance

  • Eliminated redundant /api/auth/users/me requests: the dashboard islands (navigation, my-schedule, email-bind check, mobile drawer) each had an independent Pinia instance and fired six identical requests on page load. A module-level single-flight wrapper deduplicates them into one shared request. The 401 token-clear path still runs only once.
  • /api/admin/public/site-config gets the same single-flight treatment (one request per page). Both public endpoints (/code-injection and /site-config) now return Cache-Control: private, max-age=300. Config changes are infrequent, so the browser can reuse cached responses.
  • The changelog is now loaded on demand. The build phase renders the last three months into a changelog.json static asset, so the page no longer inlines ~32 KB of changelog HTML (dashboard first-payload drops from 99.5 KB to 56.9 KB). The JSON is only fetched when the user opens the changelog or a new version is detected.
  • Uvicorn worker count raised from 1 to 2, giving concurrent logins more headroom (the business connection pool caps at 60, below PostgreSQL’s default 100).

Verification

  • Frontend lint, type-check, and production build pass; build artifacts confirm the bootstrap is served as type="module" with no classic-script import leaks.
  • After a redeploy, login, schedule loading, and avatar rendering were verified against a real account.
  • Manual browser check of the changelog modal: header and narrow-panel layout render correctly, subtitle is gone, bottom action bar works. Saving an older last_seen_version and refreshing triggers the auto-popup with the “new version” badge; identical versions don’t pop up. Console is clean.
  • Performance numbers confirmed: /api/auth/users/me dropped from 6 calls to 1 per page load, /site-config stays at 1, the changelog auto-loads once on first visit and zero times on repeat visits, and both public endpoints return the expected Cache-Control header.
  • Quality gate: frontend lint, type-check, and production build pass; backend compileall, startup smoke, and pytest 131 passed / 21 skipped (requires a live PostgreSQL instance).

v3.6.1 (2026-08-03) - Auth concurrency, migration governance, and Docker reliability

Section titled “v3.6.1 (2026-08-03) - Auth concurrency, migration governance, and Docker reliability”

Auth concurrency

  • Login and registration rate-limit counters now use SQLite/PostgreSQL atomic upserts with per-window expiry cleanup. Concurrent duplicate registrations consistently return 400.
  • Email verification codes are stored only as HMAC digests derived from the shared SECRET_KEY. Send cooldown, failure count, and single-use consumption use conditional updates; upgrades actively invalidate legacy plaintext codes.
  • Multi-worker production deployments share auth state through the database and enforce one stable SECRET_KEY, so verification-code HMACs and JWTs never go out of sync across workers.

Runtime and database reliability

  • /health now uses a dedicated single-connection PostgreSQL probe pool with a 2-second timeout, returning 503 promptly when the business pool is exhausted.
  • The legacy SQLite startup gate and its dedicated upgrader populate auth columns, shared-state tables, and indexes. Alembic revision a9f8e7d6c5b4 clears any leftover plaintext verification codes and adds rate-limit cleanup indexes.
  • PostgreSQL 18 data volume is mounted at /var/lib/postgresql. The Docker image bundles offline migration tools, excludes the real database and persistent state, and freezes frontend dependencies through frontend/bun.lock.

Discovery and migration compatibility

  • Added /llms.txt and /llms-full.txt, giving LLM/agent tooling a standard site index and a full-content dump following the llmstxt.org proposal.
  • Bootstrap validation for unversioned PostgreSQL catalogs still uses c4d5e6f7a8b9 as its baseline (excluding auth tables and users.token_version that later revisions introduce), then upgrades to head via Alembic.

Docs and verification

  • Development conventions, the full version history, and deployment docs are centralized in the Development Guide, this file, and the Deployment Guide.
  • Backend full regression: 152 passed. Frontend lint has zero errors, type-check and production build pass. A production SQLite replica was migrated to PostgreSQL 18 and smoke-tested against the app.
  • The changelog modal no longer proxies an external site at runtime. The build phase reads docs/CHANGELOG.md from the repo, renders the last three months inline, and the backend proxy endpoint has been removed.

v3.6.0 (2026-08-02) - Performance, auth hardening, and multi-worker reliability

Section titled “v3.6.0 (2026-08-02) - Performance, auth hardening, and multi-worker reliability”

Login performance and password hashing

  • Login password verification switched from bcrypt (cost=12, ~166 ms) to argon2id (~41 ms). Legacy bcrypt hashes are transparently rehashed on the next successful login.
  • The unmaintained passlib dependency was removed; hashing now uses argon2-cffi directly, with bcrypt retained only for legacy-hash compatibility.

Concurrency and response speed

  • 66 blocking endpoints were changed from async def to synchronous def (FastAPI runs them in a thread pool). Login no longer stalls the event loop (/health during login dropped from 168 ms to 10 ms) and lightweight endpoints stop serializing behind each other.
  • RequestTimingMiddleware was added; production logs now emit TIMING method path status X.Xms per request.
  • Schedule payloads shrank: personal endpoint no longer nests the full schedule/owner object (857 KB → 735 KB), team aggregation dropped from 3.47 MB to 1.52 MB (243 ms → 153 ms), the filter endpoint from 349 KB to 150 KB, and the serialization N+1 was eliminated.
  • FastAPI is adequate for the current scale; key performance baselines and re-test procedures live in the development guide.

Auth security

  • JWTs now carry a token_version (tver claim). Password changes, email password resets, and admin password resets invalidate all previously issued tokens for that user.
  • Minimum password length raised from 6 to 8 (NIST SP 800-63B); the frontend registration, forgot-password, and initial-admin forms were updated accordingly.
  • Login rate limiting (default 8 attempts / student-ID + IP / 300 s, lockout 600 s), registration IP limiting (default 10 / 600 s), and email verification codes (invalidated after 5 wrong guesses, constant-time comparison, single-use, 60-second send cooldown) all moved to shared database storage, so --workers N deployments correctly share state.
  • Added REGISTER_RATE_LIMIT_MAX_ATTEMPTS and REGISTER_RATE_LIMIT_WINDOW_SECONDS; removed AUTH_RATE_LIMIT_MAX_KEYS (database storage removes the key-count concern).

Database reliability and migration governance

  • SQLite-to-PostgreSQL import is atomic: data load, sequence resets, and row-count verification run in a single transaction. Non-empty targets are rejected and the whole import is rolled back on failure.
  • Share tables (temporary_shares, team_heatmap_shares) are now in the migration list and import order follows foreign-key dependencies, closing a silent data-loss gap.
  • Alembic governance: production PostgreSQL has exactly one live revision chain, a restricted bootstrap, and a startup gate that verifies current == head. The legacy SQLite upgrader works on a copy and atomically replaces the original.
  • Docker Compose now pins a specific PostgreSQL major version instead of tracking postgres:latest.
  • Share-visit counting uses a database atomic increment, fixing concurrent-count lost updates.
  • Same-name team schedules get a database-level unique constraint so concurrent creation produces only one row.
  • Smart-scheduling anchors consistently use the target schedule’s start_date.
  • Team create / import / member / admin operations each run in a single route-level transaction; any mid-operation failure rolls back the whole change.
  • /health now checks both the live database connection and the schema version, returning 503 until both pass.
  • Integration tests run against a real PostgreSQL instance in CI, covering migrations, concurrency, transactions, and health checks.
  • Backend Docker dependencies are frozen via uv.lock and installed with uv sync --frozen.
  • New Alembic revision f0a1b2c3d4e5 adds users.token_version and three shared-storage tables (login_rate_limits, register_rate_limits, verification_codes). Production PostgreSQL needs alembic upgrade head.
  • The legacy SQLite upgrader was updated to add the token_version column; the PostgreSQL integration test’s HEAD_REVISION tracks the new head.

Tests and documentation

  • Regression coverage expanded across JWT invalidation, rate-limit lockout, verification-code invalidation, bcrypt upgrade, the Alembic chain, and health checks. All current full-suite gates pass.

v3.5.0 (2026-08-01) - WebVPN off-campus access, dual-account auth, and academic connection security

Section titled “v3.5.0 (2026-08-01) - WebVPN off-campus access, dual-account auth, and academic connection security”

Off-campus WebVPN academic access

  • Schedule import and empty-classroom lookup now support WebVPN and direct connection modes, defaulting to the school’s WebVPN.
  • Importer and empty-classroom lookup share the JwxtAuthSession to avoid protocol drift between the two auth implementations.
  • Sessions are explicitly bound to a connection mode and display unified-identity or academic-system captchas per upstream requirements.
  • Added deployment parameters for WebVPN, direct addresses, and session stability.

Direct-connection protection for off-campus deployments

  • When the server cannot reach on-campus academic addresses, the frontend keeps but disables the “direct connection” option.
  • Clearly states direct mode is only for on-campus deployments while retaining backend direct capability for future recovery.

WebVPN + academic-system dual-account authentication

  • WebVPN mode now uses two-stage authentication: first log in to unified identity, then enter a separate academic-system account/password in the same session.
  • Schedule import and empty-classroom lookup add an auth_stage state and a dedicated WebVPN login endpoint; academic login is blocked until stage one completes.
  • Each stage handles captchas separately; password fields are cleared immediately after submission.
  • If the academic password is wrong or upstream temporarily fails, the established WebVPN tunnel is kept. Retries do not require redoing unified identity.

Tunnel verification and credential security

  • After a successful WebVPN login, the system actually probes the academic login page inside the tunnel instead of relying only on fixed domains or redirect results.
  • Captcha responses must be image type, avoiding WebVPN login pages or other HTML responses being mistaken for captchas.
  • WebVPN and academic-system credentials are used only for the current auth request; they are not written to the database, environment variables, cache, or logs.
  • Added regression tests for protocol, routes, session retries, and frontend stage switching; updated security conventions and usage docs.

v3.4.3 (2026-05-29) - Timezone consistency, team heatmap, and scheduling reliability fixes

Section titled “v3.4.3 (2026-05-29) - Timezone consistency, team heatmap, and scheduling reliability fixes”

Migration scripts and docs

  • Database migration workflow consolidated under scripts/migrations/.
  • Historical Alembic migration chain fully archived to scripts/migrations/legacy_alembic/.
  • Migration list and execution rules in the migration script notes updated.

Admin diagnostics and upload security hardening

  • Admin diagnostic endpoints add access control and input validation.
  • File upload pipeline error boundaries tightened to avoid leaking exception info to clients.

Team heatmap and share-link recovery

  • Fixed inaccurate team-heatmap aggregation, restoring correct multi-member busy/free views.
  • Share-link management restored: validity periods, permission configuration, and QR code display.

Default-schedule parsing consistency

  • Current-schedule resolution now always follows the default-schedule source of truth, with no more frontend/backend mismatches.

Timezone unification: calendar and exports follow Shanghai wall-clock time

  • Calendar view and ICS export/import flows now generate and parse events in Asia/Shanghai.
  • Fixed event time offsets across timezones that caused schedule display and export inconsistencies.

Scheduling regression scenarios preserved

  • Added a Team1 scheduling regression-test scenario document, making batch and smart scheduling repeatably verifiable.

Team scheduling preview creation-delay fix

  • Team scheduling preview no longer shows events that have not yet been created; preview matches the final result.

Team insertion drift fix

  • Batch and smart scheduling inserting into a reusable schedule no longer shifts events across weeks or dates.
  • Fixed the root cause of event misplacement when reusing an existing schedule write target.

v3.4.3 (2026-05-29) - Timezone consistency, team heatmaps, and scheduling reliability fixes

Section titled “v3.4.3 (2026-05-29) - Timezone consistency, team heatmaps, and scheduling reliability fixes”

Migration scripts and docs cleanup

  • Database migration workflow consolidated under scripts/migrations/
  • Historical Alembic migration chain archived to scripts/migrations/legacy_alembic/
  • Synced the migration list and execution rules in the migration script docs

Admin diagnostics and upload security hardening

  • Admin diagnostic endpoints gained access control and input validation
  • Tightened error boundaries in the file-upload pipeline to avoid leaking exception details to clients

Team heatmap and share-link restoration

  • Fixed inaccurate team heatmap aggregation, restoring correct multi-person busy/free views
  • Share-link management restored: validity period, permission config, and QR display

Default-schedule resolution consistency

  • Current-schedule resolution now uniformly follows the default-schedule source of truth, eliminating frontend/backend mismatches

Timezone unification: calendar and exports follow Shanghai wall-clock

  • Calendar view and ICS export/import flows now generate and parse events in the Asia/Shanghai timezone
  • Fixed cross-timezone event time drift causing schedule display and export mismatches

Scheduling regression scenarios retained

  • Added Team1 scheduling regression test scenario docs so batch and smart scheduling remain repeatably verifiable

Team scheduling preview delayed-creation fix

  • Team scheduling preview no longer shows events that have not actually been created yet, keeping preview consistent with the final result
Section titled “v3.4.2 (2026-05-28) - Default schedule, share links, and scheduling stability release”

Backend data layer and migrations

  • Schedule model adds the is_default field, supporting the default-schedule mechanism.
  • Schedule lifecycle begins deriving from start_date + total_weeks; hidden and default states are modeled separately.
  • New share and collaboration data structures added, with CRUD / schema mappings completed.
  • Historical Alembic chain archived to scripts/migrations/legacy_alembic/; default/hidden-schedule data corrections are handled by scripts/migrations/add_schedule_visibility_and_default_truth.py.

Default-schedule selection and write strategy

  • Backend admin, import_route, schedule, and schedules routes unify the default-schedule rule.
  • “Current schedule” reuses the default-schedule resolution instead of a separate judgment path.
  • Frontend ScheduleEditor adds a “set as default schedule” interaction.
  • Frontend schedule store loads the default schedule first on “My Schedule”.
  • frontend/src/types/index.ts adds default-schedule related type definitions.

Batch and smart scheduling stability fixes

  • batch_operations.py and smart_schedule.py support a consistent schedule-insert-target logic (new/default/specific).
  • Fixed smart-scheduling stability across conflicts, capacity, and week assignment.
  • Fixed batch-add and smart-scheduling modals whose inputs could not receive focus directly on first open.

Temporary availability and team-heatmap sharing

  • Backend adds temporary-availability and team-heatmap share routes, a public-access endpoint, and a reusable availability service.
  • Frontend adds TeamAvailabilityShareModal with image/link sharing, permissions, validity period, and QR code display.
  • AllTeamsViewPage, TeamViewPage, TemporaryTeamDrawer, TeamAvailabilityGrid, and TeamHeatmapDrawer are wired into the share flow.
  • New share-page entry frontend/src/pages/share.astro; the public view is hosted by PublicScheduleView.

Frontend performance and layering fixes

  • UserAvatar unified to a local-cache strategy with update-time-based invalidation, reducing duplicate avatar requests.
  • New frontend/src/utils/avatarCache.ts lowers bandwidth on member-heavy views.
  • TeamSlotDetailDrawer layering fixed so it is no longer hidden under upper drawers.

v3.4.1 (2026-05-28) - UI polish, temporary-availability search rework, and shared-free-time enhancements

Section titled “v3.4.1 (2026-05-28) - UI polish, temporary-availability search rework, and shared-free-time enhancements”

UI de-AI-ification and layout fixes

  • ScheduleGanttWeekView card height 68 px → 82 px, row spacing 78 px → 90 px, min row height 112 px → 130 px; fixes the “week x” truncation.
  • Gantt, week-list, and schedule-list views unified on the slate palette, rounded-2xl corners, soft shadows, removing the default AI-template feel.
  • TeamMemberStrip switches from circular initial-letter avatars to the UserAvatar component showing real avatars.

Temporary-availability search experience rework

  • TemporaryTeamDrawer drops watch-based auto-search in favor of a button trigger + Enter shortcut.
  • Search results show avatar (UserAvatar), name, student ID, class, and college.
  • Backend team.py search and temporary.py availability endpoints return avatar_url and college.
  • Types UserSearchResult and AvailabilitySlot member arrays add avatar_url and college.

Shared free-time enhancements

  • TeamAvailabilityGrid adds PNG export: centered “week x shared free time” title, bottom-right logo watermark (/logo.png, 140 px, opacity 0.6).
  • Clicking a busy/free cell opens the TeamHeatmapDrawer detail drawer, showing free/busy member lists (avatar + name + course info) for that slot.
  • TeamAvailabilityGrid adds a #header-left slot; the week input and export button align vertically inside the temporary-availability drawer.

Team view busy/free & heatmap integration

  • TeamViewPage merges “busy/free view” and “heatmap” into a single “Free” view mode (week/month/free), removing the standalone teamViewMode tab.
  • AllTeamsViewPage desktop view-switcher adds a “Free” button; the mobile dropdown adds a “busy/free view” option.
  • Busy/free view adds a multi-select avatar picker (select all / clear / toggle single); shows a “select members to view” prompt when none are selected.
  • Clicking “Apply filters” immediately syncs the selected members and refreshes busy/free data, showing only filtered members.
  • getWeekNumber moved from a TeamViewPage local function to the shared @/utils/date utility.

Export and clipboard fixes

  • TeamSlotDetailDrawer export area reworked into an expandable panel: multi-select export fields (name / student ID / class / college / free time) + format selection (TXT/CSV/EXCEL) + separate copy zones (name / student ID / name+student ID).
  • Excel export switched from dynamic import('xlsx') to static import * as XLSX from 'xlsx'; astro.config.mjs adds optimizeDeps.include: ['xlsx'], fixing the Vite 504 Outdated Optimize Dep error.
  • Clipboard copy adds a navigator.clipboard availability detection with a document.execCommand('copy') fallback off HTTPS, fixing Cannot read properties of undefined (reading 'writeText').
  • Busy members are simplified to orange name tags; course/room details removed.

Layering fixes

  • TeamSlotDetailDrawer z-index z-50 → z-[110] → z-[200], reliably covering TemporaryTeamDrawer (Headless UI Dialog z-[100]).

v3.4.0 (2026-05-27) - Team collaboration enhancements, batch scheduling, and smart scheduling

Section titled “v3.4.0 (2026-05-27) - Team collaboration enhancements, batch scheduling, and smart scheduling”

Team collaboration data layer

  • models.py adds Team settings fields: visibility_model, allow_member_invite, max_members, join_policy, shift_definitions, schedule_config.
  • 7 new data models: TeamScheduleTask (scheduling task), TeamShiftDefinition (shift definition), TeamScheduledEvent (scheduled-event link), TeamBatchOperation (batch-op record), TeamBatchOperationItem (batch-op detail), TeamRecurringEventRule (recurring scheduling rule), TemporaryTeam (temporary team).
  • schemas.py adds 16 Pydantic schemas covering scheduling tasks, batch ops, and temporary teams.
  • crud.py adds get_events_by_schedule_id(), create_batch_operation(), complete_batch_operation().
  • New Alembic migration b3c4d5e6f7a8: new teams columns + 7 new tables.

Batch scheduling

  • POST /api/teams/{id}/batch-events/preview: conflict preview returning per-user conflict details (day_of_week + time-overlap detection).
  • POST /api/teams/{id}/batch-events/execute: batch-creates course events, supporting skip and force conflict strategies.
  • Conflict dedup by (user_id, week, day_of_week) to avoid duplicate conflicts from multiple Event rows of the same course.
  • Auto-creates a “{team name} team schedule” Schedule for members without one, Monday-aligned with semester_start.
  • GET /api/teams/{id}/batch-operations/{id}: details merged per user (weeks/days/title), one row per user.
  • Supports the schedule_target parameter: default (active schedule) or new (new team schedule).

Smart scheduling

  • POST /api/teams/{id}/schedule-tasks/preview: runs the greedy algorithm on preview, returning member-assignment stats, failed slots, and a user-name map.
  • POST /api/teams/{id}/schedule-tasks: creates the scheduling task, auto-writing to the batch-op log.
  • Two modes: week mode (selected_weeks + shifts) and date mode (specific_dates, per-date required_count).
  • Stable greedy algorithm: groups by (name, day_of_week), takes the intersection of members available across all weeks (stable set), picks exactly needed as primary, assigns the same primary every week, and replaces only on conflicts.
  • Replacement candidates exclude already-assigned members; max_per_member applies globally.
  • Auto-infers semester_start (from selected_weeks or specific_dates).
  • FK safety: failed records use member_ids[0] instead of user_id=0.

Temporary team lookup

  • POST /api/temporary/availability: availability for any member combination without joining a team.
  • Supports date-range filtering and three visibility levels (busy_only / course_title / full_detail).
  • Frontend TemporaryTeamDrawer: quick member search, multi-member shared-free-slots view.

Team settings extensions

  • Team editor modal reworked into 3 tabs: team info, member management, team operations.
  • Configurable: visibility model, join policy (free / approval / invite), max members, member-invite toggle.
  • The team-operations tab hosts the batch- and smart-scheduling entries.

PostgreSQL migration fixes

  • Fixed Alembic initial schema missing the new teams columns and 7 tables (create_all() does not ALTER existing tables).
  • New repair_team_tables.py: an idempotent PG-repair script covering new columns, team-collaboration tables, and temporary_shares / team_heatmap_shares full DDL and required indexes.
  • sqlite_to_postgres.py updates TABLES_IN_ORDER to 18 tables, fully covering temporary_shares and team_heatmap_shares.

New frontend components

  • BatchTeamEventModal: batch-scheduling modal, HeadlessUI Dialog, conflict-preview panel, blue missing-schedule notice bar, schedule write-target selection.
  • TeamScheduleTaskModal: smart-scheduling modal, pill-style weekday selector, week/date dual-mode switch, result display (member counts + list/calendar views).
  • BatchOperationsLog: batch-op log panel with inline detail expansion (not a bottom overlay), per-user merged view.
  • TemporaryTeamDrawer: temporary-team lookup drawer, quick member search, shared-free-time view.
  • TeamAvailabilityGrid: color-coded availability grid.
  • TeamMemberSchedulePanel: member personal schedule panel.
  • TeamMemberStrip: member avatar strip (add / remove / role select).
  • TeamSlotDetailDrawer: slot detail drawer.

UI fixes and unification

  • Deleted CreatorTeamManagement.vue; functionality merged into TeamEditorModal tabs.
  • All modals unified to the HeadlessUI Dialog pattern (ModalTitleCard, bg-slate-950/40 backdrop-blur-sm backdrop, ring-1 ring-slate-200/80, rounded-xl, input-base inputs).
  • Nested-modal layering: parent Dialog uses :static to disable FocusTrap, child Dialog uses z-[200].
  • ScheduleEditor fix: parent Dialog gets :static when the delete-confirm modal opens to prevent focus stealing.
  • StackedEventsModal uses the UserAvatar component for real avatars.
  • Navigation admin section font font-medium → font-semibold.

v3.3.1 (2026-05-27) - In-app browser guidance, CI/CD auto-build, and sequence fixes

Section titled “v3.3.1 (2026-05-27) - In-app browser guidance, CI/CD auto-build, and sequence fixes”

In-app browser guidance

  • New frontend/src/utils/inAppBrowser.ts: detects WeChat, QQ, WeCom, DingTalk, and Alipay in-app browsers.
  • New frontend/src/components/InAppBrowserPrompt.vue: dual-mode guidance component (dialog + toast).
  • Dialog mode: first in-app open shows step guidance with a globe icon before the title and a horizontal three-dot-menu icon in the steps.
  • Toast mode: re-opening within the same day after the dialog was shown produces a light toast notice.
  • localStorage records the dialog timestamp; the dialog is not shown again within 24h; sessionStorage prevents re-triggering on in-app navigation.
  • Integrated pages: home, login, register, forgot password, shared schedule, my schedule, team view (7 pages).
  • Toast-system extension: Toast interface adds iconSvg and inlineSvg; ToastItem supports custom icons and inline-SVG descriptions.

CI/CD auto-build

  • New .github/workflows/docker-publish.yml: auto-builds a Docker image on push to main, tagging latest + x.y.z.
  • Pushing a v* git tag also uses that tag as the image tag.
  • AGENTS.md adds a version-management section listing the 4 version locations that must change together and the manual release flow.

PostgreSQL sequence fix

  • scripts/migrations/sqlite_to_postgres.py adds a sequence-reset step: after import, sets the sequences of all tables with an id column to MAX(id).
  • Fixes UniqueViolation on new inserts after an SQLite migration caused by unreset sequences.

v3.3.0 (2026-05-26) - PostgreSQL support, Alembic migrations, and data-migration tooling

Section titled “v3.3.0 (2026-05-26) - PostgreSQL support, Alembic migrations, and data-migration tooling”

PostgreSQL database support

  • New psycopg[binary] driver dependency; backend can connect to PostgreSQL.
  • database.py adds the pool_recycle parameter (default 1800 s) to prevent long-idle connections from being dropped by the server.
  • Engine creation logs the database type (SQLite / PostgreSQL) without leaking the connection string or password.
  • models.py association tables user_teams and team_admins add ondelete=CASCADE; PG cleans up related rows on user/team deletion.
  • models.py index fields are given explicit String(N) lengths (student_id=50, full_name=100, etc.), improving PG index efficiency.

Alembic migration system

  • Alembic initialized, reading the connection string dynamically from the DATABASE_URL env var.
  • New initial_schema migration: creates all tables on PostgreSQL; skipped on SQLite (handled by create_all).
  • New add_performance_indexes migration: composite indexes for schedules(owner_id, status), events(schedule_id, day_of_week), and login_records(user_id, login_time).
  • Future model changes go exclusively through Alembic migrations, with no more manual SQL scripts.

SQLite-to-PostgreSQL data-migration tool

  • New scripts/migrations/sqlite_to_postgres.py: validates SQLite integrity and the PostgreSQL target schema, then migrates all 18 tracked tables in FK-dependency order (including temporary_shares and team_heatmap_shares).
  • Auto-converts boolean fields (SQLite 0/1 → PG true/false).
  • PostgreSQL FK checks stay on during import; schedule_adjustments before events; session_replication_role is not used.
  • The tool only accepts empty target tables for all tracked tables; there is no --force or table-truncation mode; all inserts, sequence fixes, and count checks run in a single transaction, rolling back on any failure with a non-zero exit code.
  • New migration Runbook: full migration steps, verification, and rollback procedure.

Docker Compose rework

  • docker-compose.yml adds a db service (postgres:latest) with a healthcheck and a postgres_data persistent volume.
  • App DATABASE_URL switches to a PostgreSQL connection string; depends_on adds the db healthcheck.
  • Removed the deprecated version attribute and undefined network references.
  • New .env.example and backend/.env.example providing POSTGRES_PASSWORD, SECRET_KEY, connection-pool, and other config templates.

Docs updated

  • Deployment guide adds a “Database configuration” section: PG pool params, pg_dump / pg_restore backup/restore, SQLite-migration steps.
  • Project-intro tech stack updated: SQLite → SQLite / PostgreSQL (PostgreSQL recommended for production).

v3.2.0 (2026-05-18) - UI visual convergence, unified auth forms, and mobile navigation optimization

Section titled “v3.2.0 (2026-05-18) - UI visual convergence, unified auth forms, and mobile navigation optimization”

UI visual convergence and typography

  • Removed default shadows from base input / button / dropdown components; only overlays and modals keep shadows, producing a cleaner, flatter UI.
  • Radius system unified: inputs rounded-lg (8 px), list cards rounded-xl (12 px); removed 22 px / 28 px / 32 px oversized radii.
  • Removed BaseLayout’s global forced border-radius: 1rem; components now control their own radii.
  • Heading weights unified from font-black / font-bold down to font-semibold; body contrast improved from gray-500 to slate-600 for readability.
  • Removed all-caps labels and high tracking (e.g. “Day Agenda” → “Day Schedule”), restoring normal Chinese wording.
  • PageHeaderCard removes gradient background / shadow / ring, switching to a pure-stroke container for a lighter look.
  • Navigation sidebar switches from shadow-sm ring to a border-r divider; admin-section red lowered to a less saturated rose.
  • MyTeamsPage metrics area and team-code area flatten (no nested cards).
  • Danger color unified from red to a lower-saturation rose, reducing visual aggression.

Auth-form UI unification

  • Login / register / forgot-password forms unified to the input-base CSS class, replacing inline styles.
  • Standard radius unified from rounded-xl to rounded-lg for visual consistency.
  • Button styles simplified: gradient backgrounds and shadows removed.
  • AuthShell decorative background elements and ribbon animation removed, returning to a minimal design.
  • Color variables unified from gray to slate for palette consistency.
  • Card styles simplified: backdrop-blur and frosted-glass effects removed.

Mobile bottom tab bar

  • New MobileBottomTabBar: a fixed bottom quick-navigation bar for mobile with four high-frequency entries: schedule, team, empty classroom, profile.
  • The tab bar is fixed to the viewport bottom and shows only below the lg breakpoint (lg:hidden).
  • Supports iPhone safe-area inset (env(safe-area-inset-bottom)) to avoid obscuring the Home indicator.
  • Main content area auto-adds pb-20 bottom padding so the tab bar does not cover content.
  • The existing mobile top bar, drawer, sidebar, and desktop layout are unchanged.