Local setup, environment configuration, and deployment for Sharetopus.
- Local dev stack
- Prerequisites
- Setup
- Environment variables
- Scripts
- Type checking
- Code conventions
- Deployment
- Source files referenced
graph LR
subgraph Local["localhost:3000"]
App["Next.js Dev Server<br/>(Turbopack)"]
end
subgraph Remote["Remote Services"]
Supabase["Supabase<br/>DB + Storage"]
Clerk["Clerk<br/>Auth"]
Upstash["Upstash Redis<br/>Rate Limiting"]
end
subgraph LocalTools["Local Tooling"]
StripeCLI["Stripe CLI<br/>stripe listen --forward-to"]
InngestDev["Inngest Dev Server<br/>npx inngest-cli dev"]
end
App -->|"queries + storage"| Supabase
App -->|"session verification"| Clerk
App -->|"rate limit checks"| Upstash
StripeCLI -->|"forwards webhook events"| App
InngestDev -->|"dispatches background jobs"| App
The Next.js dev server talks to remote Supabase, Clerk, and Upstash instances. Stripe webhooks and Inngest job dispatch run locally through their respective CLI tools.
| Dependency | Purpose |
|---|---|
| Node.js 20+ | Runtime |
| Supabase project | Postgres database and file storage |
| Clerk application | Authentication and user management |
| Stripe account | Subscription billing (3 products with price IDs) |
| Inngest account | Background job processing |
| Upstash Redis instance | API rate limiting |
| Platform OAuth apps (one per platform) | LinkedIn, TikTok, Pinterest, Instagram |
git clone <repo-url>
cd sharetopus
npm install
cp .env.example .env.local
# Fill in all required values (see tables below and .env.example comments)
npm run dev # http://localhost:3000- Create a Supabase project.
- Apply the database schema (tables, RLS policies, functions). If a
Supabase_db_schemafile exists in the repo root, use it as reference. - Create a storage bucket named
scheduled-videos(or setSUPABASE_BUCKET_NAMEto your chosen name). - Copy the project URL, anon key, and service role key into
.env.local.
- Create a Clerk application and enable your preferred sign-in methods.
- Add a webhook endpoint:
{FRONTEND_URL}/api/webhooks/clerk. - Subscribe to events:
user.created,user.updated,user.deleted. - Copy the publishable key, secret key, and webhook signing secret into
.env.local. - For local development, populate
CLERK_WEBHOOK_SECRET_DEVinstead ofCLERK_WEBHOOK_SECRET.
- Create 3 products with monthly and yearly prices matching the plan config in
src/lib/types/plans.ts. - Add a webhook endpoint:
{FRONTEND_URL}/api/webhooks/stripe. - Subscribe to events:
customer.subscription.*,invoice.payment_succeeded,invoice.payment_failed. - Copy the secret key, publishable key, and webhook signing secret into
.env.local. - For local webhook testing, install the Stripe CLI and run:
stripe listen --forward-to localhost:3000/api/webhooks/stripe- Get your event key and signing key from the Inngest dashboard.
- For local development, start the dev server:
npx inngest-cli@latest dev- The Inngest serve endpoint is at
/api/inngest.
Each platform requires an OAuth app with its redirect URL set to {FRONTEND_URL}/api/social/{platform}/connect.
| Platform | Redirect URL (local) | Notes |
|---|---|---|
http://localhost:3000/api/social/linkedin/connect |
Standard OAuth | |
| TikTok | http://localhost:3000/api/social/tiktok/connect |
Requires separate dev/prod credentials (TIKTOK_CLIENT_KEY_DEV, TIKTOK_CLIENT_SECRET_DEV) |
http://localhost:3000/api/social/pinterest/connect |
Standard OAuth | |
http://localhost:3000/api/social/instagram/connect |
Use the "Instagram Login" product on Meta, not "Facebook Login" |
All variables are documented in .env.example. The tables below group them by service.
| Variable | Required | Notes |
|---|---|---|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY |
Yes | Clerk dashboard, API Keys |
CLERK_SECRET_KEY |
Yes | Clerk dashboard, API Keys |
CLERK_WEBHOOK_SECRET |
Prod | Webhook signing secret |
CLERK_WEBHOOK_SECRET_DEV |
Dev | Local dev override |
| Variable | Required | Notes |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Yes | Project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Yes | Anon/public key |
SUPABASE_SERVICE_ROLE |
Yes | Service role key (server-only) |
SUPABASE_BUCKET_NAME |
No | Default: scheduled-videos |
SUPABASE_CUSTOM_STORAGE_DOMAIN |
No | For TikTok supabase_direct media mode |
| Variable | Required | Notes |
|---|---|---|
STRIPE_SECRET_KEY |
Yes | Stripe dashboard |
STRIPE_PUBLISHABLE_KEY |
Yes | Stripe dashboard |
STRIPE_WEBHOOK_SECRET |
Prod | Webhook signing secret |
STRIPE_WEBHOOK_SECRET_DEV |
Dev | Local dev override |
| Variable | Required | Notes |
|---|---|---|
UPSTASH_REDIS_REST_URL |
Yes | Upstash Redis REST endpoint |
UPSTASH_REDIS_REST_TOKEN |
Yes | Upstash Redis REST token |
| Variable | Required | Notes |
|---|---|---|
INNGEST_EVENT_KEY |
Yes | Event dispatch key |
INNGEST_SIGNING_KEY |
Yes | Serve endpoint auth |
| Variable | Required | Notes |
|---|---|---|
LINKEDIN_CLIENT_ID |
Per platform | LinkedIn OAuth app |
LINKEDIN_CLIENT_SECRET |
Per platform | LinkedIn OAuth app |
LINKEDIN_REDIRECT_URL |
Per platform | Default: http://localhost:3000/api/social/linkedin/connect |
TIKTOK_CLIENT_KEY |
Prod | Production TikTok app |
TIKTOK_CLIENT_SECRET |
Prod | Production TikTok app |
TIKTOK_CLIENT_KEY_DEV |
Dev | Sandbox TikTok app |
TIKTOK_CLIENT_SECRET_DEV |
Dev | Sandbox TikTok app |
TIKTOK_REDIRECT_URL |
Per platform | Default: http://localhost:3000/api/social/tiktok/connect |
TIKTOK_MEDIA_SOURCE |
No | proxy (default) or supabase_direct |
PINTEREST_CLIENT_ID |
Per platform | Pinterest OAuth app |
PINTEREST_CLIENT_SECRET |
Per platform | Pinterest OAuth app |
PINTEREST_REDIRECT_URL |
Per platform | Default: http://localhost:3000/api/social/pinterest/connect |
INSTAGRAM_CLIENT_ID |
Per platform | Meta app with Instagram Login |
INSTAGRAM_CLIENT_SECRET |
Per platform | Meta app with Instagram Login |
INSTAGRAM_REDIRECT_URL |
Per platform | Default: http://localhost:3000/api/social/instagram/connect |
| Variable | Required | Notes |
|---|---|---|
CDP_API_KEY_ID |
For x402 | Coinbase CDP API key ID |
CDP_API_KEY_SECRET |
For x402 | Coinbase CDP API key secret |
CDP_WALLET_SECRET |
For x402 | CDP wallet secret |
X402_RECIPIENT_EVM |
For x402 | EVM wallet address for USDC payments |
X402_RECIPIENT_SOLANA |
For x402 | Solana wallet address for USDC payments |
X402_DEFAULT_NETWORK |
No | Default: base |
X402_FACILITATOR_URL |
No | Default: Coinbase hosted facilitator |
CDP_WEBHOOK_SIGNING_SECRET |
For x402 | HMAC verification of CDP webhook events |
| Variable | Required | Notes |
|---|---|---|
FRONTEND_URL |
Yes | Default: http://localhost:3000 |
NEXT_PUBLIC_BASE_URL |
No | Default: https://sharetopus.com |
CRON_SECRET_KEY |
Yes | Shared secret for cron auth bypass |
MEDIA_PROXY_HMAC_SECRET |
Yes | 64 hex chars. Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" |
MCP_IP_HASH_SALT |
Prod | 32 bytes base64. Generate: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" |
| Command | Description |
|---|---|
npm run dev |
Start dev server with Turbopack (next dev --turbopack) |
npm run build |
Production build (next build) |
npm run start |
Start production server (next start) |
npm run lint |
Run ESLint (next lint) |
REST API endpoints live in src/app/api/v1/. The OpenAPI spec is served at /api/v1/openapi.json. Interactive API docs (Scalar) are at /docs/api.
REST API code uses import { z } from "zod" (Zod 4). MCP code uses import { z } from "zod/v3" because @modelcontextprotocol/sdk expects Zod 3 typings. Do not mix them.
Pages in src/content/docs/*.mdx are served at /docs/<slug>. The @next/mdx loader is configured in next.config.ts with pageExtensions: ["ts", "tsx", "mdx"].
typescript.ignoreBuildErrors: true is set in next.config.ts. tsc runs in CI and pre-commit hooks, not during Vercel builds (OOM mitigation). Always run npx tsc --noEmit locally before pushing.
npx tsc --noEmitNo test framework is configured. Type checking is the primary automated verification step.
Server actions return a result object instead of throwing:
{ success: boolean; message: string; data?: T; resetIn?: number }Inngest workers throw only for retryable errors. Non-retryable failures are returned as values.
All server actions and core functions prefix log messages with the function name in brackets:
[schedulePostInternal] Creating post for user u_abc...
[deleteSupabaseFileAction] Removed file xyz from bucket
@/* maps to src/* (configured in tsconfig.json). All imports use this alias.
adminSupabase and other privileged modules use the server-only package. Importing them from a client component triggers a build error, preventing accidental exposure of the service role key.
All post-creating functions accept a createdVia parameter with one of four values: web, mcp, x402, or api. This value is stored with every post so analytics can distinguish origin.
Web server actions generate a requestId at entry and thread it through batch functions. This provides a correlation ID in logs for tracing a single user action across multiple internal calls.
Deployed to Vercel. The main branch deploys to production at sharetopus.com.
Vercel function timeout is configured per route using Next.js route segment config (export const maxDuration), not in vercel.json.
| Route | maxDuration |
|---|---|
src/app/api/mcp/[transport]/route.ts |
300s |
src/app/api/inngest/route.ts |
300s |
src/app/api/x402/register/route.ts |
60s |
src/app/api/x402/connect/route.ts |
60s |
| All other routes | Vercel default |
Development uses .env.local with *_DEV variants for webhook secrets and platform credentials:
CLERK_WEBHOOK_SECRET_DEVinstead ofCLERK_WEBHOOK_SECRETSTRIPE_WEBHOOK_SECRET_DEVinstead ofSTRIPE_WEBHOOK_SECRETTIKTOK_CLIENT_KEY_DEV/TIKTOK_CLIENT_SECRET_DEVinstead ofTIKTOK_CLIENT_KEY/TIKTOK_CLIENT_SECRET
Production uses Vercel environment variables with production keys. NODE_ENV=production selects production Stripe price IDs.
| File | What it contains |
|---|---|
package.json |
Scripts, dependencies |
tsconfig.json |
Path aliases, compiler options |
vercel.json |
Vercel deployment configuration |
.env.example |
Full list of environment variables with documentation |
src/lib/types/plans.ts |
Stripe product and price ID configuration |
src/app/api/mcp/[transport]/route.ts |
MCP server route (maxDuration 300) |
src/app/api/inngest/route.ts |
Inngest serve endpoint (maxDuration 300) |
src/app/api/webhooks/clerk/route.ts |
Clerk webhook handler |
src/app/api/webhooks/stripe/route.ts |
Stripe webhook handler |
src/app/api/webhooks/tiktok/publish/route.ts |
TikTok publish status webhook handler |
src/lib/jobs/runtimeConfig.ts |
Runtime constants including max duration |
src/app/api/v1/ |
REST API v1 route handlers |
src/app/api/v1/openapi.json/route.ts |
OpenAPI spec endpoint |
src/lib/api/rest/ |
REST API middleware, auth, DTOs, validation, webhooks |
src/content/docs/ |
MDX documentation pages (served at /docs/) |
next.config.ts |
MDX loader, pageExtensions, typescript.ignoreBuildErrors |
See also: ARCHITECTURE.md, MCP.md, ROADMAP.md