Skip to content
smovidyaPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

135 Commits

Folders and files

Repository files navigation

Election

SMO Vidya Chula election frontend (Astro) and backend (Cloudflare Workers).

Local development

Install dependencies with pnpm install. Create apps/backend/.dev.vars with a local JWT_SECRET, then initialize the local database:

pnpm --filter election-backend db:seed
pnpm dev

Open http://localhost:4321 and select Election mocks in Astro's development toolbar. If the toolbar is disabled, run pnpm --filter election-frontend exec astro preferences enable devToolbar.

  • Test voter A/B signs in immediately. Enter a custom 10-digit science student ID (ending in 23) and name, then select Switch user. These are ordinary 30-minute session tokens; eligibility and previous votes use local D1 data.
  • Sign out removes the session and returns to the home page.
  • Before / During / After freeze election time just before opening, at the midpoint, or exactly at closing. Custom time is always Bangkok time (UTC+07:00), regardless of the computer's timezone. Use real time clears the override.

Changes reload the page. Frozen time persists across navigation in the same tab. The countdown, backend voting checks, results time checks, and ballot/voter timestamps use the same time. OAuth and session expiry continue using real time. Results use resultStatus in packages/constants/src/event.ts.

The toolbar runs only under astro dev. The backend accepts development login and time headers only when ENVIRONMENT=dev; staging and production disable both. Wrangler uses local D1/KV by default. Changing users does not erase votes.

API and tests

Staging builds use APP_ENV=staging from apps/frontend/.env.staging and display a yellow testing banner on every page, plus a testing label in shared receipts. Voting is available before and after the election window on staging only. Google login, 30-minute session expiry, eligibility, and one vote per student still apply. Results keep their normal announcement restrictions. Production builds use APP_ENV=production and retain the voting window. Frontend environment flags are set during the Astro build, not from Wrangler runtime variables.

In development, POST /auth/dev-login accepts { studentId, studentName } and returns { jwtSessionToken }. Send that token as Authorization: Bearer …. An independent X-Dev-Time header accepts an ISO timestamp with seconds and an explicit timezone, such as 2026-10-05T12:00:00+07:00. Missing or invalid values fall back to the clock. This replaces the old Basic-auth time override.

Tests can pass now: () => new Date(...) to createApp without changing the system clock. Run the Worker integration tests with:

pnpm --filter election-backend test

The tests use an isolated local database and cover sessions, user switching, voting boundaries, ballot timestamps, duplicate voting, and production gates.

Production deployment

Use Cloudflare account e2069f9cc14d2fe64afc9d04ed6576bd. Production has its own KV namespace; staging retains its existing namespace. Apply db:seed:production before deploying backend changes: it creates missing schema and the ballots_record_voter trigger without deleting existing votes.

Vote submission uses one guarded SQL statement, with eligibility materialized before inserting ballots. The trigger records participation within that same statement. Concurrent duplicate submissions return voted-already; a failed ballot rolls back all ballots and participation. Keep the trigger installed when rolling back code; it is compatible with the previous writer.

export CLOUDFLARE_ACCOUNT_ID=e2069f9cc14d2fe64afc9d04ed6576bd
pnpm --filter election-backend db:seed:production
pnpm --filter election-backend deploy:production
pnpm --filter election-frontend deploy:production

A first backend deployment requires a separate production JWT_SECRET Worker secret. The frontend deployment script builds in production mode before upload. The production URLs are https://election.vidyachula.org and https://election-api.vidyachula.org. Verify Google allows the JavaScript origin https://election.vidyachula.org and, for the redirect flow, https://election.vidyachula.org/login.

The confirmed voting window is October 5, 2026, 07:00–17:00 Bangkok time; results stay hidden until resultStatus is unofficial or official and the window has closed. The integration tests also cover simultaneous submissions and statement rollback.

Publishing results

Set resultStatus in packages/constants/src/event.ts:

  • "hidden": the homepage hides results and the API rejects result requests.
  • "unofficial": the homepage displays the database tally as unofficial results.
  • "official": the homepage and API use officialElectionResult as certified results, bypassing the database tally and its cache.

All modes keep results unavailable before voting closes. Redeploy both apps using the production deployment commands above after changing the status or counts. No database seed is needed for a result-only change.

Before switching to "official", replace officialElectionResult with verified counts for every position and choice. For the current by-election, the format is:

export const officialElectionResult: OfficialElectionResult = {
  totalVotes: 0, // Replace with the certified number of voters.
  votesByPosition: [{
    position_id: "vp2",
    votesByChoice: [
      { choice: "c1", count: 0 }, // Replace all counts with certified values.
      { choice: "c2", count: 0 },
      { choice: "c3", count: 0 },
      { choice: "no-vote", count: 0 },
      { choice: "disapprove", count: 0 },
    ],
  }],
};

The empty default official result is rejected rather than published as zero votes.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages