Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
# Database
MONGODB_URI=mongodb://root:password@localhost:27017/articlify?authSource=admin
# Set to true only when MongoDB is a replica set or mongos (e.g. production). Omit or false for standalone (e.g. local Docker).
# MONGODB_USE_TRANSACTIONS=true

# Auth (NextAuth.js / Auth.js v5)
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-key-here-generate-with-openssl-rand-base64-32
# Auth (Better Auth)
# Required. Generate with: openssl rand -base64 32
BETTER_AUTH_SECRET=your-secret-key-here-generate-with-openssl-rand-base64-32
# Optional: set if app URL differs (e.g. production/stage). Use same origin for sign-in and get-session.
# BETTER_AUTH_URL=https://your-domain.com
# Optional: extra allowed origins (comma-separated). Vercel preview/stage URLs are allowed automatically via VERCEL_URL.
# BETTER_AUTH_TRUSTED_ORIGINS=https://stage.example.com,https://preview.example.com

# Storage Configuration
# Set to 'minio' for local development, 's3' for production.
Expand All @@ -20,7 +26,6 @@ S3_PUBLIC_URL=http://localhost:9000/articlify-images
S3_FORCE_PATH_STYLE=true

# Storage - AWS S3 (Production)

# Bucket must allow public read (or use CloudFront/custom domain and set S3_PUBLIC_URL).
# STORAGE_PROVIDER=s3
# S3_REGION=us-east-1
Expand All @@ -29,3 +34,11 @@ S3_FORCE_PATH_STYLE=true
# S3_BUCKET=articlify-production
# S3_PUBLIC_URL=https://articlify-production.s3.us-east-1.amazonaws.com
# S3_FORCE_PATH_STYLE=false

# Mailer
# Set to 'log' for local (logs only), 'resend' for sending via Resend.
MAILER_PROVIDER=log
# MAILER_FROM=notifications@yourdomain.com
# RESEND_API_KEY=re_xxxx (required when MAILER_PROVIDER=resend)
# MAILER_RETRY_ENABLED=true
# MAILER_RATE_LIMIT_PER_MINUTE=10
2 changes: 1 addition & 1 deletion .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -1 +1 @@
yarn commitlint --edit "$1"
commitlint --edit "$1"
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lts/krypton
113 changes: 113 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
yarn dev # Start development server
yarn build # Build for production
yarn test # Run tests (Vitest, jsdom env)
yarn test:watch # Run tests in watch mode
yarn lint # Run ESLint
yarn lint:fix # Fix ESLint errors
yarn type-check # TypeScript check (tsc --noEmit)
yarn docker:up # Start MongoDB + MinIO via Docker Compose
yarn docker:down # Stop Docker services
yarn email:dev # Preview emails at localhost:3001
```

To run a single test file:
```bash
yarn vitest run src/path/to/file.test.ts
```

## Architecture

This is a **Next.js 16 App Router** app using **Feature-Sliced Design (FSD) v2.1**. The package manager is **Yarn 4**.

### Path aliases
- `~/*` → `./src/*`
- `app/*` → `./app/*`
- `i18n/*` → `./i18n/*`
- `server/*` → `./server/*`

### Layer map

```
root/app/ → Next.js routing glue ONLY (page.tsx, layout.tsx)
src/app/ → App init: global providers (tRPC, theme, i18n)
src/views/ → Page-level orchestration (data fetch + widget assembly)
src/widgets/ → Large reusable UI blocks (header, editor, smart-list)
src/features/ → User-action slices (auth, avatar, article CRUD)
src/entities/ → Domain slices: article, user, tag (model/api/ui)
src/shared/ → Context-agnostic: ui primitives, lib, config, types
server/ → tRPC root (context, procedures, appRouter)
```

**Import direction is strictly top → bottom.** No upward or sideways cross-slice imports. `shared` must not import from any other layer.

### FSD slice structure

Each entity/feature follows:
```
slice-name/
model/ # types, schemas, Zod validators
api/ # repository, service, tRPC router
ui/ # React components for that slice
```

### Backend data flow

tRPC procedures in `server/` call `entities/<name>/api/<name>.router.ts` → `<name>.service.ts` → `<name>.repository.ts` (Mongoose).

Three procedure types in `server/trpc.ts`:
- `publicProcedure` — open
- `protectedProcedure` — requires `ctx.session`
- `adminProcedure` — requires `role === 'admin'`

tRPC app router is assembled at `server/routers/index.ts` (`article`, `user`).

### tRPC client usage

Client component:
```typescript
import { trpc } from '~/shared/api/trpc/client';
const { data } = trpc.article.list.useQuery({ page: 1 });
```

Server component:
```typescript
import { createServerCaller } from '~/shared/api/trpc/server';
const caller = await createServerCaller();
const articles = await caller.article.list({ page: 1 });
```

### Auth

**Better Auth** (`better-auth`) handles authentication. Config lives at `auth.ts` (root). API route at `app/api/auth/[...all]/`. Client helpers at `src/shared/api/auth-client.ts`.

### Environment config

Server env is validated with Zod at startup via `src/shared/config/env/server.ts` → `getServerConfig()`. All server code should use this instead of `process.env` directly. Required vars: `MONGODB_URI`, `BETTER_AUTH_SECRET`.

### Emails

React Email templates live in `src/shared/emails/`. The mailer package is `@basedest/mailer`. In development, `MAILER_PROVIDER=log` prints emails to the console; set `MAILER_PROVIDER=resend` with `RESEND_API_KEY` for real sending. Preview with `yarn email:dev`.

### i18n

`next-intl` with locale-based routing under `app/[locale]/`. Routing config in `i18n/`. All user-facing text must go through translation keys.

### Testing

Vitest with jsdom. Tests live alongside source as `*.test.ts(x)` inside `src/`. Setup file: `src/shared/test/setup.ts`. MSW is available for API mocking.

## Key conventions

- Folders: `kebab-case`. Components: `kebab-case` (older components may be `PascalCase`, but new ones should be `kebab-case`). Hooks: `useXxx`.
- Views: ≤ ~150 LOC. `root/app` files: ≤ ~30 LOC.
- All new business logic belongs in a service; routers only validate input and call services.
- Do not add logic to `root/app` pages — delegate to a `src/views` component.
- Do not create a generic `components/` folder; use the appropriate FSD layer.
- Commit messages follow Conventional Commits (enforced by commitlint).
Loading