-
Notifications
You must be signed in to change notification settings - Fork 1
Home
Instra is an open-source social media management platform — schedule posts, generate AI captions, extend functionality with plugins, and monitor performance with built-in analytics.
| Section | Description |
|---|---|
| Getting Started | Setup, env vars, first run |
| Architecture | Project structure & design decisions |
| Features | What Instra can do |
| Plugin System | Building and distributing plugins |
| Database | Schema overview |
| Cache & Rate Limiting | Redis strategy |
| i18n | Internationalization |
| Contributing | How to contribute |
- Node.js 20+
- PostgreSQL
- Upstash Redis
- Supabase project
- OpenRouter API key (for AI captions)
git clone https://github.com/your-org/instra.git
cd instra
npm install
cp .env.example .env.local # fill in values
npx prisma migrate dev
npm run devOpen http://localhost:3000.
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL |
✅ | PostgreSQL connection string |
NEXTAUTH_SECRET |
✅ | NextAuth signing secret |
NEXTAUTH_URL |
✅ | App base URL |
NEXT_PUBLIC_SUPABASE_URL |
✅ | Supabase project URL |
SUPABASE_SERVICE_ROLE_KEY |
✅ | Supabase admin key |
UPSTASH_REDIS_REST_URL |
✅ | Upstash Redis URL |
UPSTASH_REDIS_REST_TOKEN |
✅ | Upstash Redis token |
OPENROUTER_API_KEY |
✅ | AI caption generation |
OPENROUTER_MODEL |
❌ | Model slug (default: openai/gpt-oss-120b:free) |
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| Language | TypeScript 5 |
| UI | React 19, Tailwind CSS v4, Framer Motion |
| Auth | NextAuth v5 + @auth/prisma-adapter
|
| Database | PostgreSQL via Prisma 7 |
| Storage | Supabase Storage |
| Cache / Rate-limit | Upstash Redis |
| AI | Vercel AI SDK + OpenRouter |
| Plugin sandbox | isolated-vm |
| Deployment | Vercel |
/app — App Router pages, layouts, API routes
/components — Shared UI components
/features — Feature modules (auth, ai, campaigns, posts, …)
/plugins — Plugin loader, registry, context API
/lib — Prisma client, Supabase, cache, rate-limit, AI client
/prisma — schema.prisma + migrations
/locales — /en/common.json, /pl/common.json
/docs — Per-module documentation
/types — Global TypeScript types
-
No direct DB/fetch in components — only through services in
/lib/api/. -
Feature isolation — each feature lives in
/features/<name>/and exports only throughindex.ts. - Server Actions for mutations — no REST endpoints for internal writes.
-
Cache-first reads — all heavy DB queries go through
getOrSet()in/lib/cache.
Users can create posts with text (up to 2,200 characters) and up to 10 images. The feed is publicly accessible at /feed (ISR, 60s revalidation) and cached in Redis with a 300s TTL. Likes and cursor-based pagination are supported.
Rate limits: 10 posts/hour per user, 60 likes/minute per user.
Campaigns allow posts to be scheduled for future publishing. A Vercel Cron job polls nextRunAt and dispatches PUBLISH_POST or WEBHOOK actions. Webhook targets are validated against an SSRF guard before delivery.
A single Server Action (generateCaption) calls OpenRouter via the Vercel AI SDK. Auth check → rate limit → Zod validation → API call. The model is configurable via OPENROUTER_MODEL.
Bell icon in the header with unread count badge. Users can manage per-channel preferences (email, in-app) in Settings.
Dashboard metrics with aggregated stats. Reports can be scheduled (ReportRun table); results are stored and viewable in the dashboard.
NextAuth v5 with Prisma adapter. Supports credentials (email + password, bcrypt) and email verification flow. Role system: USER and ADMIN.
Plugins extend Instra through a sandboxed runtime (isolated-vm). They register UI widgets, routes, and menu items via PluginContext — they never access the database, filesystem, or network directly.
/plugins/my-plugin/
manifest.json — metadata, permissions, widget slots
index.ts — exports init() and optionally destroy()
components/
hooks/
types/
README.md
| Slot | Location |
|---|---|
dashboard:top |
Top of dashboard |
dashboard:sidebar |
Dashboard sidebar |
dashboard:bottom |
Bottom of dashboard |
settings:general |
General settings page |
settings:advanced |
Advanced settings page |
header:actions |
Header action area |
profile:menu |
User profile menu |
-
manifest.jsonvalidated by JSON Schema before loading. - All plugin actions written to an audit log.
-
ErrorBoundarywraps every widget slot. - No server-side plugin execution without a sandbox.
Full API reference: /docs/plugins.md
Managed exclusively through Prisma ORM — no raw SQL. Schema lives in prisma/schema.prisma.
| Model | Purpose |
|---|---|
User |
Accounts, roles, profile |
Post |
Social media posts (text + media) |
Media |
Images attached to posts (Supabase Storage) |
Like |
Post likes (user × post, unique) |
Campaign |
Scheduled publishing campaigns |
Plugin / PluginVersion
|
Plugin registry |
PluginInstallation |
Per-user plugin installs |
Notification |
In-app notifications |
Report / ReportRun
|
Analytics reports |
Schema changes: edit prisma/schema.prisma → npx prisma migrate dev → update /docs/database.md.
Both use Upstash Redis with isolated key namespaces:
| Concern | Namespace | TTL |
|---|---|---|
| DB query cache | instra:cache:* |
300s (db), 900s (api) |
| Rate limiting | instra:rl:* |
sliding window |
Cache pattern:
const data = await getOrSet('instra:cache:feed', () => fetchFeed(), 300)After any Prisma mutation call invalidatePrefix() to bust the relevant cache keys. Details in /docs/cache.md.
All UI strings go through t("key") — no hardcoded text in JSX. Translation files:
/locales/en/common.json
/locales/pl/common.json
To add a new language: create /locales/<lang>/common.json and register it in /lib/i18n/config.ts.
| Command | Description |
|---|---|
npm run dev |
Development server |
npm run build |
Production build |
npm run lint |
ESLint |
npm run test |
Vitest (single run) |
npm run test:watch |
Vitest (watch) |
- CONTRIBUTING.md — how to contribute
- CHANGELOG.md — release history
-
/docs/— per-module documentation