On this page
Changelog
Full version history of SDNUChronoSync.
Last updated:
All versions are archived here; the homepage only displays the last three months.
Changelog
Section titled “Changelog”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/querysession 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=gradedeep-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-alpineservice (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_URLis 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_atversion 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_versionincrements atomically in SQL; productionSECRET_KEYrequires ≥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 supportIdempotency-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/schedulingidempotency_key - PostgreSQL deployments must run
alembic upgrade headbefore startup; production direct academic endpoints refuse plaintext HTTP (unless explicitlyJWXT_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/--accentetc., defined inBaseLayout.astro, with:rootfor light andhtml.darkfor dark auto-switching; added layer tokens--layer-dropdown(1200)/popover(1250)/modal(1400)/toast(1500) - Tailwind
darkMode: 'class';neutralunifies scatteredgray/slate,primaryabsorbssky, removedsecondaryfuchsia 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: supportsstretchfor equal-width buttons, sliding indicator capsule with smooth switching (based on AinOfficialWiki Tabs pattern), migrated SystemSettings / TeamEditorModal / ScheduleAdjuster PickerPopoverpanel andCodeEditordark 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, containerdark:bg-neutral-900recessed track + activedark: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+handleCloseguard - Fixed main button
hover:bg-primary-50white 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/hiddentoggle
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
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”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
- The project directory structure and API reference are now maintained on the Architecture & Tech Reference page instead of being inlined in the README. The GitHub link is standardized to CelPlume/SDNUChronoSync.
In-site link migration
- Tutorial URLs in the README, full changelog, and llms.txt/llms-full.txt are moved from
hs.cnies.orgto 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 rawimportstatement tripped the browser’s “Cannot use import statement outside a module” error andinitAuthnever ran. The fix exposes the build-time changelog version via an inline variable script and loads the bootstrap as a propertype="module"bundle. - Avatar cross-origin cache requests now omit credentials (
credentials: 'omit'). Alist returnsAccess-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
ModalTitleCardheader (icon + title), a narrowermax-w-3xlpanel withrounded-xlcorners 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 thehasNewVersionflag set by the bootstrap script was never visible to the modal. The fix has the bootstrap broadcast achronosync:changelog-newwindow event that the modal listens for and reacts to.
Login and load performance
- Eliminated redundant
/api/auth/users/merequests: 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-configgets the same single-flight treatment (one request per page). Both public endpoints (/code-injectionand/site-config) now returnCache-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.jsonstatic 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-scriptimportleaks. - 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_versionand 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/medropped from 6 calls to 1 per page load,/site-configstays at 1, the changelog auto-loads once on first visit and zero times on repeat visits, and both public endpoints return the expectedCache-Controlheader. - Quality gate: frontend lint, type-check, and production build pass; backend
compileall, startup smoke, andpytest131 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
/healthnow 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
a9f8e7d6c5b4clears 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 throughfrontend/bun.lock.
Discovery and migration compatibility
- Added
/llms.txtand/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
c4d5e6f7a8b9as its baseline (excluding auth tables andusers.token_versionthat 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.mdfrom 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
passlibdependency was removed; hashing now usesargon2-cffidirectly, withbcryptretained only for legacy-hash compatibility.
Concurrency and response speed
- 66 blocking endpoints were changed from
async defto synchronousdef(FastAPI runs them in a thread pool). Login no longer stalls the event loop (/healthduring login dropped from 168 ms to 10 ms) and lightweight endpoints stop serializing behind each other. RequestTimingMiddlewarewas added; production logs now emitTIMING method path status X.Xmsper 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(tverclaim). 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 Ndeployments correctly share state. - Added
REGISTER_RATE_LIMIT_MAX_ATTEMPTSandREGISTER_RATE_LIMIT_WINDOW_SECONDS; removedAUTH_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.
/healthnow 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.lockand installed withuv sync --frozen. - New Alembic revision
f0a1b2c3d4e5addsusers.token_versionand three shared-storage tables (login_rate_limits,register_rate_limits,verification_codes). Production PostgreSQL needsalembic upgrade head. - The legacy SQLite upgrader was updated to add the
token_versioncolumn; the PostgreSQL integration test’sHEAD_REVISIONtracks 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
JwxtAuthSessionto 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_stagestate 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/Shanghaitimezone - 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
v3.4.2 (2026-05-28) - Default schedule, share links, and scheduling stability release
Section titled “v3.4.2 (2026-05-28) - Default schedule, share links, and scheduling stability release”Backend data layer and migrations
Schedulemodel adds theis_defaultfield, supporting the default-schedule mechanism.Schedulelifecycle begins deriving fromstart_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 byscripts/migrations/add_schedule_visibility_and_default_truth.py.
Default-schedule selection and write strategy
- Backend
admin,import_route,schedule, andschedulesroutes unify the default-schedule rule. - “Current schedule” reuses the default-schedule resolution instead of a separate judgment path.
- Frontend
ScheduleEditoradds a “set as default schedule” interaction. - Frontend schedule store loads the default schedule first on “My Schedule”.
frontend/src/types/index.tsadds default-schedule related type definitions.
Batch and smart scheduling stability fixes
batch_operations.pyandsmart_schedule.pysupport 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
TeamAvailabilityShareModalwith image/link sharing, permissions, validity period, and QR code display. AllTeamsViewPage,TeamViewPage,TemporaryTeamDrawer,TeamAvailabilityGrid, andTeamHeatmapDrawerare wired into the share flow.- New share-page entry
frontend/src/pages/share.astro; the public view is hosted byPublicScheduleView.
Frontend performance and layering fixes
UserAvatarunified to a local-cache strategy with update-time-based invalidation, reducing duplicate avatar requests.- New
frontend/src/utils/avatarCache.tslowers bandwidth on member-heavy views. TeamSlotDetailDrawerlayering 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
ScheduleGanttWeekViewcard 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.
TeamMemberStripswitches from circular initial-letter avatars to theUserAvatarcomponent showing real avatars.
Temporary-availability search experience rework
TemporaryTeamDrawerdrops 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.pysearch andtemporary.pyavailability endpoints returnavatar_urlandcollege. - Types
UserSearchResultandAvailabilitySlotmember arrays addavatar_urlandcollege.
Shared free-time enhancements
TeamAvailabilityGridadds 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
TeamHeatmapDrawerdetail drawer, showing free/busy member lists (avatar + name + course info) for that slot. TeamAvailabilityGridadds a#header-leftslot; the week input and export button align vertically inside the temporary-availability drawer.
Team view busy/free & heatmap integration
TeamViewPagemerges “busy/free view” and “heatmap” into a single “Free” view mode (week/month/free), removing the standaloneteamViewModetab.AllTeamsViewPagedesktop 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.
getWeekNumbermoved from aTeamViewPagelocal function to the shared@/utils/dateutility.
Export and clipboard fixes
TeamSlotDetailDrawerexport 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 staticimport * as XLSX from 'xlsx';astro.config.mjsaddsoptimizeDeps.include: ['xlsx'], fixing the Vite 504 Outdated Optimize Dep error. - Clipboard copy adds a
navigator.clipboardavailability detection with adocument.execCommand('copy')fallback off HTTPS, fixingCannot read properties of undefined (reading 'writeText'). - Busy members are simplified to orange name tags; course/room details removed.
Layering fixes
TeamSlotDetailDrawerz-indexz-50→z-[110]→z-[200], reliably coveringTemporaryTeamDrawer(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.pyadds 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.pyadds 16 Pydantic schemas covering scheduling tasks, batch ops, and temporary teams.crud.pyaddsget_events_by_schedule_id(),create_batch_operation(),complete_batch_operation().- New Alembic migration
b3c4d5e6f7a8: newteamscolumns + 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, supportingskipandforceconflict strategies.- Conflict dedup by
(user_id, week, day_of_week)to avoid duplicate conflicts from multipleEventrows of the same course. - Auto-creates a “{team name} team schedule”
Schedulefor members without one, Monday-aligned withsemester_start. GET /api/teams/{id}/batch-operations/{id}: details merged per user (weeks/days/title), one row per user.- Supports the
schedule_targetparameter:default(active schedule) ornew(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-daterequired_count). - Stable greedy algorithm: groups by
(name, day_of_week), takes the intersection of members available across all weeks (stable set), picks exactlyneededas primary, assigns the same primary every week, and replaces only on conflicts. - Replacement candidates exclude already-assigned members;
max_per_memberapplies globally. - Auto-infers
semester_start(fromselected_weeksorspecific_dates). - FK safety: failed records use
member_ids[0]instead ofuser_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
teamscolumns and 7 tables (create_all()does notALTERexisting tables). - New
repair_team_tables.py: an idempotent PG-repair script covering new columns, team-collaboration tables, andtemporary_shares/team_heatmap_sharesfull DDL and required indexes. sqlite_to_postgres.pyupdatesTABLES_IN_ORDERto 18 tables, fully coveringtemporary_sharesandteam_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 intoTeamEditorModaltabs. - All modals unified to the HeadlessUI Dialog pattern (
ModalTitleCard,bg-slate-950/40 backdrop-blur-smbackdrop,ring-1 ring-slate-200/80,rounded-xl,input-baseinputs). - Nested-modal layering: parent Dialog uses
:staticto disable FocusTrap, child Dialog usesz-[200]. ScheduleEditorfix: parent Dialog gets:staticwhen the delete-confirm modal opens to prevent focus stealing.StackedEventsModaluses theUserAvatarcomponent for real avatars.Navigationadmin section fontfont-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:
Toastinterface addsiconSvgandinlineSvg;ToastItemsupports custom icons and inline-SVG descriptions.
CI/CD auto-build
- New
.github/workflows/docker-publish.yml: auto-builds a Docker image on push tomain, tagginglatest+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.pyadds a sequence-reset step: after import, sets the sequences of all tables with anidcolumn toMAX(id).- Fixes
UniqueViolationon 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.pyadds thepool_recycleparameter (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.pyassociation tablesuser_teamsandteam_adminsaddondelete=CASCADE; PG cleans up related rows on user/team deletion.models.pyindex fields are given explicitString(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_URLenv var. - New
initial_schemamigration: creates all tables on PostgreSQL; skipped on SQLite (handled bycreate_all). - New
add_performance_indexesmigration: composite indexes forschedules(owner_id, status),events(schedule_id, day_of_week), andlogin_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 (includingtemporary_sharesandteam_heatmap_shares). - Auto-converts boolean fields (SQLite
0/1→ PGtrue/false). - PostgreSQL FK checks stay on during import;
schedule_adjustmentsbeforeevents;session_replication_roleis not used. - The tool only accepts empty target tables for all tracked tables; there is no
--forceor 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.ymladds adbservice (postgres:latest) with a healthcheck and apostgres_datapersistent volume.- App
DATABASE_URLswitches to a PostgreSQL connection string;depends_onadds the db healthcheck. - Removed the deprecated
versionattribute and undefined network references. - New
.env.exampleandbackend/.env.exampleprovidingPOSTGRES_PASSWORD,SECRET_KEY, connection-pool, and other config templates.
Docs updated
- Deployment guide adds a “Database configuration” section: PG pool params,
pg_dump/pg_restorebackup/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 cardsrounded-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-bolddown tofont-semibold; body contrast improved fromgray-500toslate-600for readability. - Removed all-caps labels and high tracking (e.g. “Day Agenda” → “Day Schedule”), restoring normal Chinese wording.
PageHeaderCardremoves gradient background / shadow / ring, switching to a pure-stroke container for a lighter look.Navigationsidebar switches fromshadow-sm ringto aborder-rdivider; admin-sectionredlowered to a less saturatedrose.MyTeamsPagemetrics area and team-code area flatten (no nested cards).- Danger color unified from
redto a lower-saturationrose, reducing visual aggression.
Auth-form UI unification
- Login / register / forgot-password forms unified to the
input-baseCSS class, replacing inline styles. - Standard radius unified from
rounded-xltorounded-lgfor visual consistency. - Button styles simplified: gradient backgrounds and shadows removed.
AuthShelldecorative background elements and ribbon animation removed, returning to a minimal design.- Color variables unified from
graytoslatefor palette consistency. - Card styles simplified:
backdrop-blurand 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
lgbreakpoint (lg:hidden). - Supports iPhone safe-area inset (
env(safe-area-inset-bottom)) to avoid obscuring the Home indicator. - Main content area auto-adds
pb-20bottom padding so the tab bar does not cover content. - The existing mobile top bar, drawer, sidebar, and desktop layout are unchanged.