跳到正文
GitHub·· 6 天前AI 评分12

Free AP Practice:学生自建的 SvelteKit AP 刷题应用

5cisummai/freeappractice

AI 导读

学生开发者 Ajay Saravanan 推出 Free AP Practice,一个基于 SvelteKit + Svelte 5 的 AP 练习应用,可生成 AI 选择题并提供应用内辅导、书签和答题历史。

正文

Free AP Practice is a student-built SvelteKit app for generating AP practice questions, tracking progress, and studying with instant feedback. The project is founded and maintained by Ajay Saravanan.

The goal is straightforward: make AP prep feel faster, more personalized, and more accessible for students who do not want to pay for another prep subscription just to get quality practice.

What's included

  • Public marketing pages: landing, about, subjects, summer study guide, blog, stats, privacy, terms, and changelog.
  • Authenticated app at /app for dashboard, practice, progress, question history, resources, and settings.
  • SEO practice landing pages under /practice/[...slug] (class and unit pages with internal linking).
  • AI-generated MCQs with an in-app tutor, bookmarks, and attempt history.
  • Optional Super membership with personalized MCQ/FRQ tutoring, Coach, and a weekly study plan.
  • Public generation stats at /stats (backed by /api/question/generation-stats).
  • Better Auth for email/password and Google sign-in (including Google One Tap when configured).
  • SvelteKit API routes for questions, signed-in user data, tutoring, and bug reports.
  • Markdown blog posts in src/content/blog/; question content is stored in Neon PostgreSQL for practice, bookmarks, and history.

Tech stack

  • SvelteKit + Svelte 5
  • TypeScript
  • Tailwind CSS
  • Neon PostgreSQL + Drizzle ORM (@neondatabase/serverless HTTPS driver)
  • Better Auth for sessions, email flows, and OAuth
  • Vercel AI SDK with an OpenAI-compatible API for question generation and tutor responses (OpenAI or LM Studio locally)
  • Resend for transactional email
  • AWS S3-compatible storage for one-time legacy question imports
  • Stripe + Better Auth Stripe plugin for Super subscriptions, Mem0 for optional tutor memory, and Upstash Redis for short-lived AI controls
  • Vercel for deployment (@sveltejs/adapter-vercel)

Getting started

Prerequisites

  • Node.js 20+
  • Bun
  • A Neon PostgreSQL project/branch for the environment you are running

Setup

  1. Install dependencies:

    bun install
  2. Copy the example env file and fill in your values:

    cp .env.example .env
  3. (Optional) Start local PostgreSQL and Redis services with compose.yaml:

    docker compose up -d

    The deployed app uses the Neon DATABASE_URL. The local PostgreSQL service is available at postgresql://postgres:password@localhost:5432/freeappractice for SQL inspection and migration work; the runtime must use a Neon branch URL so @neondatabase/serverless can send requests over HTTPS.

  4. Start the dev server:

    bun dev
  5. Build for production:

    bun run build
  6. Apply pending schema migrations (when drizzle/ SQL changed):

    bun run db:apply

    Point DATABASE_URL at the intended Neon branch before running. On push to main or staging, CI applies migrations before Vercel deploys.

  7. Run the unit test suite:

    bun test:unit
  8. Run the deployed question-generation smoke test:

    SMOKE_BASE_URL=https://staging.example.com bun run smoke:question

    This checks the public practice page and the same POST /api/question path used by the browser. It retries temporary pool-warming responses and verifies that the returned question has a prompt, four A–D choices, a correct answer, and a question ID. Set SMOKE_CLASS and SMOKE_UNIT when checking another supported course or unit.

Useful scripts

Command Purpose
bun check Type-check with svelte-check
bun test:unit Run Vitest unit tests
bun run db:apply Apply committed Drizzle migrations to Neon
bun lint / bun format ESLint and Prettier

Environment variables

Copy .env.example to .env. Required for a working local setup:

Variable Purpose
DATABASE_URL Neon PostgreSQL connection string used by Drizzle over HTTPS
BETTER_AUTH_SECRET Session signing secret (min 32 chars; generate with openssl rand -base64 32)
BETTER_AUTH_URL App base URL for auth callbacks (e.g. http://localhost:5173)
OPEN_AI_KEY API key for the configured provider (any value works for local LM Studio)
OPENAI_BASE_URL OpenAI-compatible API base URL (defaults to https://api.openai.com/v1)

Model choices for every AI use case are plain exported constants in src/lib/ai/ai-models-config.ts.

Commonly needed for full functionality:

Variable Purpose
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET Google OAuth
PUBLIC_GOOGLE_CLIENT_ID Google One Tap on the client
RESEND_API_KEY / RESEND_FROM Transactional email
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_REGION / AWS_S3_BUCKET Optional legacy S3 import tooling
AWS_SESSION_TOKEN Optional temporary/assumed-role credentials
PUBLIC_BASE_URL Canonical site URL
GITHUB_BUG_REPORT_TOKEN GitHub Issues API for in-app bug reports
CRON_SECRET Secures Vercel cron routes (Authorization: Bearer …); required in production for scheduled jobs

Super launch configuration

Super is disabled until Stripe credentials and both price IDs are present. Configure STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_SUPER_MONTHLY_PRICE_ID, STRIPE_SUPER_ANNUAL_PRICE_ID, DATABASE_URL, OPEN_AI_KEY, KV_REST_API_URL, KV_REST_API_TOKEN, and REDIS_IDENTIFIER_SECRET in Vercel before enabling the Super Flags. Enable the pgvector extension and apply the Drizzle migrations to each Neon database before enabling memory.

In Stripe, configure the Customer Portal to allow cancellation at period end, restoration before the end of the current period, and switching between the two Super prices at the next renewal. Point the Stripe webhook to Better Auth's /api/auth/stripe/webhook route and enable at least checkout.session.completed, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, invoice.paid, invoice.payment_failed, invoice.payment_action_required, and invoice.finalization_failed. Configure Stripe Tax registrations before enabling automatic tax. The app enables automatic tax and does not enable promotion codes.

Tutor memory uses the Mem0 OSS SDK with OpenAI extraction/embeddings and an Upstash Vector index. The app keeps Development, Preview, and Production memories isolated inside the shared index with deployment-scoped namespaces and metadata. Connect the Upstash Vector integration to all three Vercel environments and pull the variables locally with vercel env pull .env.development.local --environment=development when setting up a new machine.

Upstash Redis is limited to fast, disposable control-plane data: rate limits, monthly AI-turn reservations, single-flight locks, idempotency keys, and 30-minute Coach approvals. Upstash Vector stores optional tutor memories; Neon PostgreSQL and Stripe remain the other durable stores. Question-selection and grading logic do not use Redis. For local Redis testing, run an Upstash-compatible Serverless Redis HTTP proxy in front of a local Redis server and point the same KV_REST_API_URL and KV_REST_API_TOKEN variables at that proxy.

Pool fill targets, leases, and daily LLM budget are coded in src/lib/question-bank/pool-constants.ts (not env). See docs/question-pool-runbook.md.

Coach images use a connected private Vercel Blob store in deployments. Locally, run bun run storage:local to start RustFS and create its private S3 bucket, then add the COACH_IMAGE_S3_* values from .env.example to .env.local. The console is at http://127.0.0.1:9001. Enable the coach-images Vercel flag for Development to use the composer. Uploads accept one PNG, JPEG, or WebP up to 2 MB, with limits of 8192 pixels per side and 16 megapixels. Images are normalized to WebP without metadata. Each chat permits 50 images and each account 500; deleting chats frees storage. Chat/account deletion preserves attachment paths for retryable cleanup in the Super maintenance job. Failed, unattached uploads are eligible for cleanup after one hour.

Local AI with LM Studio

To run question generation and tutoring against a local model instead of OpenAI:

  1. Start LM Studio and load a model with the local server enabled (default http://localhost:1234/v1).

  2. Set in .env:

    OPENAI_BASE_URL=http://localhost:1234/v1
    OPEN_AI_KEY=lm-studio

    Set the model identifier shown in LM Studio in src/lib/ai/ai-models-config.ts.

API overview

Routes live under src/routes/api as SvelteKit server endpoints.

Authentication (Better Auth)

All auth flows are handled by Better Auth at /api/auth/* (sign-up, sign-in, sign-out, email verification, password reset, Google OAuth, session management, account deletion, and email change). Use the Better Auth client in src/lib/auth/client.ts from the browser; do not call legacy /api/auth/register or /api/auth/login endpoints — those were removed in v1.4.1.

Signed-in user data (/api/me/*)

These routes require an active Better Auth session:

Method Route Description
GET /api/me/stats User practice statistics
GET /api/me/progress Progress by subject/unit
GET /api/me/history Paginated MCQ attempt history. Query params: page (default 1), limit (default 50, max 200), optional apClass
POST /api/me/record-attempt Record an answer attempt
GET /api/me/bookmarks List bookmarked questions
POST /api/me/bookmarks Add or remove a bookmark

Questions

Method Route Description
POST /api/question Select an active AP question from the pool
GET /api/question/generation-stats Public read-only generation counters

Other

Method Route Description
POST /api/tutor/chat, /api/tutor/greeting AI tutor assistant
POST /api/bug-report Submit bug reports as GitHub Issues (rate-limited per IP)
GET /health Health check

Optional Vercel Analytics and Speed Insights load only after a user opts in inside the app.

Bug reports

The in-app bug report form creates GitHub Issues in this repository via the GitHub Issues API.

  • Reports are labeled automatically with bug and a severity label.
  • A server-side per-IP rate limit reduces spam.
  • Set GITHUB_BUG_REPORT_TOKEN with Issues: Read & Write access scoped to this repository.

Deployment

Deploy to Vercel as a SvelteKit app:

  1. Connect the repository to Vercel.
  2. Set production environment variables (at minimum DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL, and OPEN_AI_KEY). Runtime only — the Vercel build does not need database access.
  3. Add DATABASE_URL (and STAGING_DATABASE_URL if you use the staging branch) as GitHub Actions secrets. Pushes to main / staging run bun run db:apply in CI before Vercel bundles the app.
  4. Vercel runs bun run build (vite build) via the default SvelteKit integration.

BETTER_AUTH_URL and PUBLIC_BASE_URL must match your production domain. If you change hosting, update the SvelteKit adapter and these notes together.

Branding & usage

The Freeappractice name, logo, and brand identity are proprietary and are exclusively reserved for Ajay Saravanan. The source code is open, but the brand is not.

What you are welcome to do

  • Star or like the repository
  • Fork the repository and use the code as a starting point for your own project
  • Open pull requests, report bugs, and contribute improvements back to this repository

What is not permitted

  • Launching any public site, app, or service under the name Freeappractice, Free AP Practice, or any confusingly similar name or branding
  • Creating a derivative product that implies it is Freeappractice or a related official product
  • Claiming any affiliation, endorsement, or partnership with Freeappractice or Ajay Saravanan without explicit written permission

If you fork the code to build something of your own, give it a completely different name and make it clear that it is an independent project unrelated to Freeappractice.

Notes

  • Question generation and grading are API-driven; the UI is fully SvelteKit.
  • The landing page routes authenticated users into /app.
  • The blog and summer study guide are meant to bridge reading content with active practice.
  • Practice landing pages and the stats page are public; signed-in features live under /app.

来源:GitHub · github.com