|
|
||
|---|---|---|
| .agents/skills | ||
| .cdp | ||
| .claude/skills | ||
| .windsurf/skills | ||
| app | ||
| components | ||
| hooks | ||
| lib | ||
| prisma | ||
| public | ||
| shots | ||
| types | ||
| .dockerignore | ||
| .env.example | ||
| .gitattributes | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| Dockerfile | ||
| README.md | ||
| auth.ts | ||
| components.json | ||
| docker-compose.yml | ||
| docker-entrypoint.sh | ||
| eslint.config.mjs | ||
| next.config.ts | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.mjs | ||
| prisma.config.ts | ||
| proxy.ts | ||
| skills-lock.json | ||
| software.md | ||
| tsconfig.json | ||
README.md
Organize
A self-hosted, installable PWA for staying organized: an extended to-do list and notes organizer laid out as a Kanban-style board.
- Categories are the board's lanes (e.g. "To Do", "In Progress", "Done").
- Groups are the cards within a lane — each has a color, a markdown notes panel, and a to-do list, and can be dragged between lanes.
- To-Dos live inside a Group, with an optional details field.
Accounts are required (there's no anonymous/local-only mode — the board needs a database to persist to), and the first account ever created on a given instance automatically becomes its administrator.
Features
- Kanban board with drag-and-drop: reorder Categories, and drag Groups between/within Categories
- Per-Group markdown notes and a to-do list with a completion progress indicator
- Light/dark theme (follows system by default)
- Per-account theme: the theme you pick (theme menu, bottom-left) is saved on that account's profile, so each account keeps its own look — after an account toggle you land in the other account's theme. Applies from the first frame of each page load (no flash)
- Installable PWA (add-to-home-screen); requires a live connection to the server for its database, so there's no offline mode
- Credentials-based accounts (email + password), with an Admin page to:
- change a user's role (Administrator / User / Pending), email, or password, or delete their account
- control how the site handles new sign-ups (see Sign-up modes below)
- Profile page (
/profile): set a first/last name and a profile photo, shown in the menu on the left (the photo replaces the initial-letter avatar when set). The account's default theme lives on the profile too — pick it from the theme menu (bottom-left of the sidebar); it persists per account, not per browser - Account linking (Profile page): link multiple accounts on the same
server so one person can juggle more than one identity.
- Request Account Link — ask to link by the other account's email; the request shows "awaiting confirmation" until they respond
- Requested Account Link — the recipient's list of pending requests, with Create Account Link, Deny Account Link, or Deny and Block Account Link (blocks that account from requesting again; blocks can be lifted from the Blocked Account Link Requests section)
- Linked Accounts — the other side of each confirmed link, with Toggle (signs this browser out and back in as that account — it never asks for that account's password) and Remove Link (either linked account can do it)
Tech stack
Next.js 16 (App Router) · React 19 · TypeScript · Tailwind CSS 4 · PostgreSQL via Prisma 7 · Auth.js v5 (Credentials provider, JWT sessions) · dnd-kit · base-ui
Quick start (Docker)
This is the recommended way to run Organize. All you need is Docker with the Compose plugin.
-
Clone the repo
git clone https://git.brianfertig.com/brianfertig/Organize.git cd Organize -
Create your
.envcp .env.example .envThen open
.envand:- Generate a real
AUTH_SECRET(used to sign session cookies) — for example withopenssl rand -base64 32— and paste it in. - Optionally change
POSTGRES_PASSWORDfrom the placeholder.
See Configuration below for what each variable does.
- Generate a real
-
Start everything
docker compose up -dThis builds the app image, starts Postgres, waits for it to be healthy, then starts the app — which applies any pending database migrations automatically on every boot before serving traffic. No separate migration or setup step is needed.
-
Open the app at http://localhost:3000 (or whatever
APP_PORTyou set) and sign up. The very first account created becomes the site's administrator; everyone after that follows whatever sign-up mode is currently set (Open, by default).
Updating
git pull
docker compose up -d --build
--build matters here: without it, Compose will keep running whatever
image it already built and won't notice that the source changed.
Stopping
docker compose down
Postgres's data lives in ./data/postgres on the host (a bind mount, not
a Docker volume), so it survives docker compose down — back up that
directory if you want a safety net before major upgrades.
Configuration
All configuration is via environment variables, read from .env by
Docker Compose (see .env.example for the template).
| Variable | Used by | Purpose |
|---|---|---|
AUTH_SECRET |
app |
Encrypts/signs session cookies. Required in production — generate with openssl rand -base64 32. |
POSTGRES_USER |
db |
Postgres superuser created on first init. |
POSTGRES_PASSWORD |
db |
Password for POSTGRES_USER. Change this from the .env.example placeholder. |
POSTGRES_DB |
db |
Database name created on first init. |
APP_PORT |
app (host mapping only) |
Host port the app is published on, e.g. 3000 for http://localhost:3000. Defaults to 3000 if unset. The container always listens on 3000 internally regardless of this value. |
DATABASE_URL |
host-side tooling only | Prisma connection string for running things like npx prisma studio from your own machine. Not used by the app container itself — see note below. |
Note on
DATABASE_URL: insidedocker-compose.yml, theappservice is given its ownDATABASE_URLthat points at thedbservice over the Compose network (db:5432), built from thePOSTGRES_*variables — it ignores theDATABASE_URLin.env. The.envvalue (pointed atlocalhost:5432) is only there for convenience if you want to run Prisma commands, or the app itself, directly on the host. Since the bundleddbservice doesn't publish port 5432 to the host by default, that only works once you either add aports: ["5432:5432"]mapping to thedbservice, or pointDATABASE_URLat a Postgres instance you're running yourself.
Sign-up modes
The Admin page (/admin, administrators only) has a "New sign-ups"
setting with four modes:
- Open (default) — anyone can sign up and immediately gets the
Userrole. - Approved — anyone can sign up, but starts as
Pendingand can't log in until an administrator changes their role toUserorAdministrator. - Confirmed — not implemented yet (shown disabled in the UI); intended to gate new accounts on email confirmation instead of manual approval.
- Closed — sign-ups are turned off entirely: the sign-up form and any links to it are removed, and the sign-up action itself refuses new accounts even if called directly.
The very first account on a fresh instance always becomes an administrator, regardless of the current sign-up mode — otherwise there'd be no one able to approve anyone.
Local development (without Docker)
Running the app directly on your host is mainly useful for editing code with hot reload. You'll still need a Postgres instance it can reach.
- Node 24 (matching the Docker image; no
enginesfield enforces this, but it's what's tested) andnpm install. - A reachable Postgres. The simplest option is to add a port mapping
to the
dbservice indocker-compose.yml(ports: ["5432:5432"]) and run just that service:docker compose up -d db. Then setDATABASE_URLin.envto match (the.env.exampledefault ofpostgresql://organize:organize@localhost:5432/organizewill work as-is if you keep the default Postgres credentials). - Apply migrations:
Against a fresh database this also runsnpx prisma migrate devprisma/seed.tsautomatically, creatingdev@example.com/password123as an administrator with a small sample board. If it doesn't (e.g. you'd already applied every migration before), runnpx prisma db seedto trigger it explicitly. Don't run either against a database you care about. - Run the dev server:
Open http://localhost:3000.npm run dev
Other useful scripts: npm run build (production build), npm run start
(serve a production build), npm run lint.
Project structure
app/ Next.js App Router routes: (app) is the signed-in board + admin,
(auth) is login/signup
components/board/ The Kanban board: categories, groups, to-dos, drag-and-drop
components/admin/ Admin page: user table, role menu, sign-up mode settings
lib/actions/ Server Actions (auth, categories, groups, todos, notes, admin)
lib/ Shared server helpers (db client, auth helpers, settings, colors)
prisma/ Schema, migrations, and the dev-only seed script
auth.ts Auth.js configuration (Credentials provider, JWT session callbacks)
proxy.ts Route protection (redirects signed-out users to /login, etc.)