File size: 9,486 Bytes
fe5ae84
52b412b
 
 
 
 
 
 
 
2b62b0c
72e7de2
cfc59e4
2b62b0c
72e7de2
 
 
 
 
 
 
 
 
 
 
2a80c9b
72e7de2
 
 
 
 
2b62b0c
72e7de2
2b62b0c
72e7de2
 
cfc59e4
 
 
2b62b0c
 
 
72e7de2
 
 
2b62b0c
72e7de2
 
2b62b0c
72e7de2
2b62b0c
 
 
 
 
72e7de2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cfc59e4
 
 
72e7de2
cfc59e4
 
2b62b0c
 
cfc59e4
 
 
72e7de2
cfc59e4
72e7de2
cfc59e4
 
72e7de2
cfc59e4
 
72e7de2
 
 
 
 
 
 
cfc59e4
 
72e7de2
 
cfc59e4
 
72e7de2
cfc59e4
72e7de2
 
2b62b0c
cfc59e4
72e7de2
 
cfc59e4
72e7de2
 
 
 
cfc59e4
72e7de2
 
cfc59e4
72e7de2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cfc59e4
 
72e7de2
cfc59e4
 
72e7de2
cfc59e4
72e7de2
ef46e44
cfc59e4
 
72e7de2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cfc59e4
72e7de2
 
 
 
 
 
 
 
cfc59e4
 
 
 
2b62b0c
cfc59e4
72e7de2
 
cfc59e4
72e7de2
 
 
 
 
 
 
 
 
cfc59e4
 
2b62b0c
72e7de2
cfc59e4
 
72e7de2
 
 
 
 
 
 
 
 
 
 
 
 
ef46e44
72e7de2
 
 
 
14be139
 
 
 
 
 
72e7de2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2b62b0c
72e7de2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a80c9b
72e7de2
 
 
 
 
 
 
 
 
 
 
7bcfa1f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
---
title: Pram API
emoji: πŸš€
colorFrom: green
colorTo: blue
sdk: docker
app_port: 3000
---
<p align="center">
  <img src="./apps/web/public/logo.png" alt="Pram" width="200" />
</p>

<h1 align="center">Pram</h1>

<p align="center">
  <strong>AI-powered multi-language test practice platform</strong><br />
  Generate authentic exam questions, run mock tests, and track your progress β€” all in one place.
</p>

<p align="center">
  <a href="#getting-started">Getting Started</a> Β·
  <a href="#features">Features</a> Β·
  <a href="#supported-exams">Exams</a> Β·
  <a href="#project-structure">Structure</a> Β·
  <a href="#support">Support</a> Β·
  <a href="#license">License</a>
</p>

---

## What is Pram?

**Pram** is an open-source platform for practicing CPNS (Seleksi Calon Pegawai Negeri Sipil) exams. Users can generate questions for TWK, TIU, and TKP with AI, save them into question packages, take timed mock tests, and review results with detailed explanations in **Bahasa Indonesia**.

The project is a **Turborepo monorepo** managed with **Bun**. The web app talks to a **Hono + tRPC** backend backed by **PostgreSQL**, **Redis** (job queue), and **Better Auth**.

## Features

- **AI question generation** β€” Quick mode (single-pass) and Agentic mode (multi-step pipeline)
- **CPNS Focus** β€” TWK, TIU, and TKP sections
- **20+ question formats** β€” Multiple choice, true/false, fill-in-blank, and more
- **Question bank & packages** β€” Organize generated questions into shareable or private packages
- **Mock tests & attempts** β€” Timed practice sessions with scoring and review
- **Analytics & leaderboard** β€” Track performance over time
- **User-managed AI keys** β€” Bring your own OpenAI-compatible API key
- **PWA** β€” Installable progressive web app

## Supported Exam Sections

| Section | Type | Notes |
|---------|------|-------|
| TWK | Tes Wawasan Kebangsaan | Nasionalisme, Integritas, Bela Negara, Pilar Negara, Bahasa Indonesia |
| TIU | Tes Intelegensia Umum | Verbal, Numerik, Figural |
| TKP | Tes Karakteristik Pribadi | Pelayanan Publik, Jejaring Kerja, Sosial Budaya, TIK, Profesionalisme, Anti Radikalisme |

## Tech Stack

| Layer | Technology |
|-------|------------|
| Runtime & package manager | [Bun](https://bun.sh) 1.3+ |
| Frontend | React 19, Vite, [TanStack Router](https://tanstack.com/router) |
| Backend | [Hono](https://hono.dev), [tRPC](https://trpc.io) |
| Database | PostgreSQL + [Drizzle ORM](https://orm.drizzle.team) |
| Queue | Redis + BullMQ (AI generation jobs) |
| Auth | [Better Auth](https://www.better-auth.com) (email/password) |
| UI | [shadcn/ui](https://ui.shadcn.com) in `packages/ui` |
| AI | OpenAI-compatible API (`packages/ai`) |
| Build | Turborepo + tsdown |

## Prerequisites

- **[Bun](https://bun.sh)** `1.3.11` or later (see `packageManager` in root `package.json`)
- **PostgreSQL** 15+
- **Redis** 7+ (required for background AI generation jobs)
- **SMTP server** (required for email verification and password reset)

> Use `bun` for all install and script commands. Do not use `pnpm`, `npm`, or `yarn` unless a global tool explicitly requires it.

## Getting Started

### 1. Clone and install

```bash
git clone https://github.com/<your-org>/pram.git
cd pram
bun install
```

### 2. Start PostgreSQL and Redis

The repo includes a Docker Compose file for local development:

```bash
bun run db:start
```

This starts PostgreSQL on port `5432` and Redis on port `6379`. See [`packages/db/docker-compose.yml`](./packages/db/docker-compose.yml) for defaults.

Alternatively, point `DATABASE_URL` and `REDIS_URL` at your own instances.

### 3. Configure environment

Copy the example env files:

```bash
cp apps/server/.env.example apps/server/.env
cp apps/web/.env.example apps/web/.env
```

Edit **`apps/server/.env`**:

```env
# PostgreSQL (matches docker-compose defaults)
DATABASE_URL=postgresql://postgres:password@localhost:5432/pram

# Redis (defaults to redis://localhost:6379 if omitted)
REDIS_URL=redis://localhost:6379

# Auth β€” generate random strings β‰₯ 32 characters
BETTER_AUTH_SECRET=your-random-secret-at-least-32-chars
BETTER_AUTH_URL=http://localhost:3000
CORS_ORIGIN=http://localhost:3001

# Encrypts user AI API keys at rest β€” β‰₯ 32 characters
API_KEY_ENCRYPTION_KEY=your-encryption-key-at-least-32-chars

# SMTP (required for sign-up verification & password reset)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your-smtp-user
SMTP_PASS=your-smtp-password
SMTP_FROM=noreply@example.com

# Optional: platform-wide AI fallback (users normally set keys in Settings UI)
# PLATFORM_AI_API_KEY=
# PLATFORM_AI_BASE_URL=
# PLATFORM_AI_MODEL=
```

Edit **`apps/web/.env`**:

```env
VITE_SERVER_URL=http://localhost:3000
```

### 4. Push database schema

```bash
bun run db:push
```

Optionally seed reference data (exam types, sections):

```bash
bun run db:seed
```

### 5. Run the dev servers

```bash
bun run dev
```

| Service | URL |
|---------|-----|
| Web app | [http://localhost:3001](http://localhost:3001) |
| API server | [http://localhost:3000](http://localhost:3000) |

Sign up, verify your email, then open **Settings** to add your OpenAI-compatible API key before generating questions.

## AI Configuration

AI provider keys are **not** stored in server `.env` by default. Each user configures their own key in the **Settings** page. The server encrypts keys with `API_KEY_ENCRYPTION_KEY`.

Generation supports any **OpenAI-compatible** endpoint (OpenAI, OpenRouter, local LM Studio, etc.).

Two generation modes are available:

| Mode | Description |
|------|-------------|
| **Quick** | Single LLM call with schema validation and repair |
| **Agentic** | Multi-step pipeline: plan β†’ shared passage β†’ parallel question shards β†’ validation |

AI logic lives in [`packages/ai/`](./packages/ai/).

## Project Structure

```
pram/
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ web/              # Frontend (React + TanStack Router) β€” port 3001
β”‚   └── server/           # Backend entry (Hono + tRPC) β€” port 3000
β”œβ”€β”€ packages/
β”‚   β”œβ”€β”€ ai/               # Prompts, schemas, quick & agentic pipelines
β”‚   β”œβ”€β”€ api/              # tRPC routers, queue workers, business logic
β”‚   β”œβ”€β”€ auth/             # Better Auth configuration
β”‚   β”œβ”€β”€ config/           # Shared TypeScript configs
β”‚   β”œβ”€β”€ db/               # Drizzle schema, migrations, docker-compose
β”‚   β”œβ”€β”€ env/              # Environment validation (Zod)
β”‚   └── ui/               # Shared shadcn/ui components & design tokens
β”œβ”€β”€ AGENTS.md             # Contributor guide for AI agents & developers
└── turbo.json
```

Internal imports use the `@pram/*` workspace namespace. Apps should not import from each other directly β€” share code through `packages/*`.

## Available Scripts

Run from the **repository root**:

| Command | Description |
|---------|-------------|
| `bun run dev` | Start web + server in development |
| `bun run dev:web` | Start web app only |
| `bun run dev:server` | Start server only |
| `bun run build` | Build all packages |
| `bun run check-types` | TypeScript check across the monorepo |
| `bun test` | Run all tests via Turborepo |
| `bun run db:push` | Push Drizzle schema to PostgreSQL |
| `bun run db:generate` | Generate migration files |
| `bun run db:migrate` | Run migrations |
| `bun run db:seed` | Seed reference data (exam types, sections) |
| `bun run db:studio` | Open Drizzle Studio |
| `bun run db:start` | Start PostgreSQL + Redis (Docker Compose) |
| `bun run db:stop` | Stop Docker Compose services |

## Deployment

Production deploys use **two Coolify applications** from this repo (web + API server), plus **PostgreSQL** and **Redis**. The server does not serve the frontend build; the web app calls the API via `VITE_SERVER_URL`.

See **[`deploy/COOLIFY.md`](./deploy/COOLIFY.md)** for step-by-step Coolify setup, environment variables, Nixpacks configs (`deploy/server.nixpacks.toml`, `deploy/web.nixpacks.toml`), and a first-deploy checklist.

## Development

### Type checking

```bash
bun run check-types
```

### Testing

```bash
bun test
```

Tests use **Bun's built-in test runner**. Unit tests live in `src/__tests__/` within each package. Integration tests in `packages/api` use PGlite (in-memory PostgreSQL).

### Adding shadcn/ui components

From the project root:

```bash
npx shadcn@latest add accordion dialog -c packages/ui
```

Import shared components:

```tsx
import { Button } from "@pram/ui/components/button";
```

Design tokens and global styles: [`packages/ui/src/styles/globals.css`](./packages/ui/src/styles/globals.css).

### PWA assets

```bash
cd apps/web && bun run generate-pwa-assets
```

## Contributing

Contributions are welcome! Before opening a PR:

1. Read [`AGENTS.md`](./AGENTS.md) for architecture, conventions, and common pitfalls.
2. Run `bun run check-types` and `bun test`.
3. Keep changes focused and match existing code style.

For AI-related changes, see the **AI Generation Context** section in `AGENTS.md` β€” adding a new question format touches schemas, prompts, attempt normalization, and frontend constants.


## License

This project is licensed under the **[GNU Affero General Public License v3.0 (AGPL-3.0)](./LICENSE)**.

If you run a modified version as a network service, you must make the corresponding source code available to users of that service.

---

<p align="center">
  Built with Bun, React, Hono, and tRPC.
</p>