Quick Reference: AI Marketing Platform
Architecture Overview
The platform is a multi-service SaaS with two main services:
api/— Node.js/Fastify API. Handles post generation, publishing, OAuth, and runs the in-process bot module.front-end/— Vue 3 SPA. Client-facing dashboard.
Supabase is the single database/auth backend. The Aimee bot runs in-process inside the API (not as a separate container) using LangChain + Telegraf.
Bot Architecture (api/src/bot/)
Telegram → TelegramAdapter.onMessage()
→ resolve user_id via messaging_users table
→ load chat_history from Supabase
→ invoke LangChain tool-calling agent
→ agent calls service functions directly
→ TelegramAdapter.sendMessage()
→ save turn to chat_history
Files:
| File | Purpose |
|---|---|
bot/index.ts | Adapter registry + boot (startBot/stopBot) |
bot/agent.ts | LangChain tool-calling agent (Aimee) |
bot/tools.ts | Service functions wrapped as LangChain tools |
bot/memory.ts | Conversation state (chat_history table) |
bot/cron.ts | node-cron: briefing, reminders, summary |
bot/logger.ts | Namespaced pino logger |
bot/channels/telegram.ts | Telegraf adapter |
bot/prompts/system.ts | Main agent system prompt |
Cron jobs (run when BOT_ENABLED=true):
- 9 AM daily — morning briefing to each active user
- 10 AM daily — approval reminders for posts pending >24h
- 9 AM Monday — weekly summary to each active user
Key API Routes
Post Generation: /generate_post, /regenerate_post, /edit_post, /publish_post, GET /post/:id
Client Data: /client/:id/context, /client/:id/posts, /client/:id/media, /client/:id/analytics
Telegram: /telegram/pair, /telegram/unpair, GET /telegram/status
Approval: /post/approve, /posts/pending-reminders
Brand Voice: /client/:id/brand-voice
Publishing: /publish, /publish_post
Content: /scrape, /search_images
Tracking: /track/open, /track/click
Webhooks: /webhooks/resend
Admin: GET /admin/customers, POST /admin/customers/:id/trigger-briefing
Database Tables (Key)
| Table | Purpose | Key Fields |
|---|---|---|
customer_customer | Clients/companies | id, name, description, industries |
customer_campaign | Campaigns per customer | start_date, end_date, target_post_count, cadence_goal |
customer_posts | Post records | status (new→pending_review→approved→scheduled→sent) |
customer_platform_post | Per-platform content | platform, content, images, character_count |
messaging_users | Channel pairings (Telegram etc.) | user_id, channel, external_id, is_active |
chat_history | Bot conversation turns | user_id, channel, external_id, role, content |
client_brand_voice | Brand rules | rule_type (tone/terminology/audience/style), rule_text |
client_content_asset | Uploaded/scraped assets | asset_type (image/document/video), url, content |
post_analytics | Performance metrics | metric_type, metric_value |
post_feedback | Client feedback on drafts | feedback_type (approve/reject/request_edit) |
customer_integration | OAuth credentials per client | integration, metadata (JSONB, AES-256 encrypted) |
marketing_calendar | Industry events/holidays | event_name, event_date, industry |
Environment Variables
All accessed via api/src/config/env.ts. Key vars:
# Database & Auth
SUPABASE_URL=
SUPABASE_KEY=
SUPABASE_SERVICE_ROLE_KEY= # Bypasses RLS — server-side bot ops only
# LLM
LLM_PROVIDER=ollama|gemini # Default: ollama
GOOGLE_API_KEY= # Gemini
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=llama2
OPENROUTER_API_KEY= # Multi-model strategy
# Bot
BOT_ENABLED=true # Enables Telegraf + cron jobs
TELEGRAM_BOT_TOKEN= # Telegraf bot token
CRON_TIMEZONE=UTC
# Publishing
GHOST_API_URL=
GHOST_ADMIN_API_KEY=
RESEND_API_KEY=
RESEND_FROM_EMAIL=
RESEND_FROM_NAME=
RESEND_WEBHOOK_SECRET=
# OAuth
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
# Content
UNSPLASH_ACCESS_KEY=
SERPAPI_API_KEY=
# Security
INTEGRATION_ENCRYPTION_KEY= # 32-byte hex, AES-256-GCM
API_KEY= # Shared secret for internal workers/scheduler
# URLs
API_BASE_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3547
# Cloudflare Browser Rendering
CF_API_TOKEN=
CF_ACCOUNT_ID=
# Error Tracking
SENTRY_DSN=
Commands
api/
pnpm dev # Start dev server with tsx watch (port 8000)
pnpm build # tsc compile to dist/
node --test # Run legacy tests (tests/*.test.ts)
pnpm test:routes # Run Vitest route tests
# Single legacy test:
node --test tests/createEmailTemplate.test.ts
# Single Vitest route test:
pnpm vitest run tests/routes/phase2.test.ts
front-end/
pnpm dev # Vite dev server on port 3547
pnpm build # Vite production build
pnpm test # Runs test:tsc + test:unit in parallel
pnpm test:unit # Vitest unit tests (happy-dom)
pnpm test:tsc # vue-tsc type check
pnpm test:e2e # Playwright E2E tests
# Single unit test:
pnpm vitest run tests/unit/<file>.spec.ts
# Single E2E test:
pnpm playwright test tests/e2e/<file>.spec.ts
File Structure (Key Paths)
api/src/
├── index.ts # Server entry + route registration
├── config/env.ts # All env vars
├── bot/ # In-process bot module (Aimee)
│ ├── index.ts
│ ├── agent.ts
│ ├── tools.ts
│ ├── memory.ts
│ ├── cron.ts
│ ├── channels/telegram.ts
│ └── prompts/
├── routes/ # Fastify route plugins
│ ├── posts.ts
│ ├── clients.ts
│ ├── content.ts
│ ├── telegram.ts
│ ├── integrations.ts
│ ├── tracking.ts
│ ├── webhooks.ts
│ └── admin.ts
├── agents/ # LLM content generation
├── integrations/ # Publishing adapters
├── modules/ # Business logic
├── services/ # Service layer
└── utils/ # Supabase client, LLM factory, etc.
front-end/src/
├── pages/ # File-based routes (unplugin-vue-router)
│ ├── auth/ # Login, signup, profile
│ └── app/ # Dashboard pages
├── components/
│ ├── ui/ # shadcn-vue components (auto-imported)
│ └── pages/ # Page-specific components
├── composables/ # Auto-imported composables
├── stores/ # Pinia stores (auto-imported)
├── utils/ # Auto-imported utilities
└── css/app.css # Tailwind v4 @theme tokens (source of truth for design)
Publishing Adapters
| File | Platform | Credentials location |
|---|---|---|
integrations/twitter.ts | Twitter/X | customer_integration.metadata (api_key, access_token…) |
integrations/linkedin.ts | access_token + metadata.person_urn | |
integrations/facebook.ts | access_token + metadata.page_id | |
integrations/mailchimp.ts | Mailchimp | metadata (api_key, list_id, server_prefix…) |
integrations/ghost-adapter.ts | Ghost CMS | global env vars (GHOST_API_URL, GHOST_ADMIN_API_KEY) |
All adapters are invoked via POST /publish in src/routes/content.ts.