A personal productivity platform combining a daily focus tracker and a bookmark manager, accessible from the web and a Chrome extension.
- Daily Focus — pick up to 3 tasks to complete today. Resets automatically at midnight UTC.
- Bookmark Manager — save URLs with auto-fetched metadata (title, description, favicon), organize into folders, tag, and search.
- Chrome Extension — one-click bookmark saving from any page.
- URL Shortener — create short links with click tracking.
- Full-text Search — search across bookmark titles, descriptions, URLs, and tags.
| Layer | Technology |
|---|---|
| Backend | NestJS + TypeScript + Prisma + PostgreSQL |
| Frontend | Next.js 16 (App Router) + Tailwind CSS v4 |
| Extension | Chrome MV3 + React + Vite |
| Auth | JWT (access token + httpOnly cookie refresh token) |
| Queue | BullMQ + Redis (async metadata fetching) |
| Deploy | Railway (API) + Vercel (Web) |
git clone https://github.com/sanudin-dev/focusmark.git
cd focusmark
pnpm installCopy the example files and fill in your values:
cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.env
cp apps/extension/.env.example apps/extension/.envapps/api/.env
DATABASE_URL=postgresql://user:password@localhost:5432/focusmark
REDIS_URL=redis://localhost:6379
JWT_ACCESS_SECRET=your_secret_here
JWT_REFRESH_SECRET=your_secret_here
JWT_ACCESS_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
PORT=3001
ALLOWED_ORIGINS=http://localhost:3000apps/web/.env
NEXT_PUBLIC_API_URL=http://localhost:3001apps/extension/.env
VITE_API_URL=http://localhost:3001docker compose up -dpnpm --filter api prisma migrate devpnpm dev| App | URL |
|---|---|
| Web | http://localhost:3000 |
| API | http://localhost:3001 |
| Swagger | http://localhost:3001/api/docs |
Build the extension and load it into Chrome:
pnpm --filter extension build- Open
chrome://extensions - Enable Developer mode
- Click Load unpacked
- Select
apps/extension/dist/
focusmark/
├── apps/
│ ├── api/ ← NestJS backend
│ ├── web/ ← Next.js frontend
│ └── extension/ ← Chrome Extension
├── packages/
│ ├── types/ ← Shared TypeScript types & Zod schemas
│ ├── constants/ ← Shared validation limits & magic numbers
│ └── utils/ ← Shared utility functions (countdown, tag parsing)
├── docker-compose.yml
├── turbo.json
└── package.json
All routes except /auth/* and /s/:slug require a Bearer token.
| Method | Route | Description |
|---|---|---|
| POST | /auth/register |
Create account |
| POST | /auth/login |
Login |
| POST | /auth/refresh |
Rotate refresh token |
| POST | /auth/logout |
Invalidate session |
| GET | /focus |
Today's focuses |
| POST | /focus |
Add focus (max 3/day) |
| PATCH | /focus/:id |
Mark done/undone |
| DELETE | /focus/:id |
Delete focus |
| GET | /bookmarks |
List bookmarks (paginated) |
| POST | /bookmarks |
Save bookmark |
| PATCH | /bookmarks/:id |
Update bookmark |
| DELETE | /bookmarks/:id |
Delete bookmark |
| GET | /folders |
List folders |
| POST | /folders |
Create folder |
| GET | /search?q= |
Full-text search |
| GET | /short-links |
List short links |
| POST | /short-links |
Create short link |
| DELETE | /short-links/:id |
Delete short link |
| GET | /s/:slug |
Redirect (public) |
# Run a specific app only
pnpm --filter api dev
pnpm --filter web dev
pnpm --filter extension build --watch
# Type-check all apps
pnpm --filter api build
pnpm --filter web type-check
# Generate Prisma client after schema changes
pnpm --filter api prisma generate
# Create a new migration
pnpm --filter api prisma migrate dev --name your_migration_name