System Architecture¶
MyPost is built as a modern, containerized microservices application designed for self-hosted deployment.
High-Level Overview¶
┌─────────────────────────────────────────────────────────────────┐
│ External Traffic (HTTPS) │
└─────────────────────────────┬───────────────────────────────────┘
│
┌─────────▼─────────┐
│ Traefik │ ← TLS termination
│ (Reverse Proxy) │
└─────────┬─────────┘
│
┌─────────▼─────────┐
│ Gateway (Nginx) │ ← Request routing
│ /api/* → API │
│ /* → Web │
└─────────┬─────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌─────▼─────┐ ┌───────▼───────┐ ┌──────▼──────┐
│ Web │ │ API │ │ Worker │
│ (SPA) │ │ (Hono) │ │ (Background)│
│ React + │ │ TypeScript │ │ Jobs │
│ Vite │ │ REST API │ │ │
└───────────┘ └───────┬───────┘ └──────┬──────┘
│ │
┌─────────────────────┼────────────────────┤
│ │ │
┌─────▼─────┐ ┌───────▼───────┐ ┌──────▼──────┐
│ MinIO │ │ PostgreSQL │ │ Redis │
│ (S3) │ │ (Database) │ │ (Queue) │
└───────────┘ └───────────────┘ └─────────────┘
Service Components¶
Gateway (Nginx)¶
Purpose: Routes requests to appropriate services
| Route | Service | Description |
|---|---|---|
/api/* |
API | REST API endpoints |
/health |
API | Health check |
/* |
Web | React SPA |
Configuration: nginx.conf (in project root)
Web (Frontend)¶
Technology: React + TypeScript + Vite + TailwindCSS
Features: - Mixpost-inspired dark theme UI - Responsive design (mobile-first) - Role-aware rendering - Real-time updates via WebSocket
Key Routes:
| Route | Component | Description |
|-------|-----------|-------------|
| / | Dashboard | Overview and stats |
| /compose | Composer | Create/edit posts |
| /calendar | Calendar | Schedule view |
| /media | Media Library | Asset management |
| /analytics | Analytics | Reports and insights |
| /settings | Settings | Configuration |
API (Backend)¶
Technology: Hono (TypeScript) + Drizzle ORM
Architecture:
src/
├── routes/ # API endpoints
│ ├── auth.ts # Authentication
│ ├── posts.ts # Post CRUD
│ ├── brands.ts # Brand management
│ └── ...
├── middleware/ # Request processing
│ ├── auth.ts # JWT validation
│ ├── permissions.ts # RBAC checks
│ └── audit.ts # Activity logging
├── workers/ # Background jobs
│ ├── publish.ts # Post publishing
│ └── token-refresh.ts # OAuth refresh
├── adapters/ # Social network APIs
│ ├── facebook.ts
│ ├── instagram.ts
│ ├── x.ts
│ └── linkedin.ts
└── db/
└── schema/ # Drizzle schemas
Worker (Background Jobs)¶
Responsibilities: - Post publishing at scheduled times - OAuth token refresh (before expiry) - Analytics data collection - Media processing
Queue System: Redis-backed with BullMQ
Data Model¶
Entity Hierarchy¶
Tenant (System-level)
└── Workspace (Organization)
└── Brand (Social presence)
├── Social Accounts
├── Posts
└── Media Assets
Core Tables¶
| Table | Purpose |
|---|---|
users |
User accounts and authentication |
workspaces |
Top-level organization units |
brands |
Social media brands/identities |
social_accounts |
Connected network accounts |
posts |
Content to be published |
post_targets |
Post → Account mappings |
media_assets |
Uploaded images/videos |
roles |
Permission templates |
audit_events |
Activity logging |
Key Relationships¶
-- User → Workspace (many-to-many via workspace_users)
workspace_users (user_id, workspace_id, role_id)
-- Workspace → Brand (one-to-many)
brands (workspace_id)
-- Brand → Social Account (one-to-many)
social_accounts (brand_id)
-- Post → Social Account (many-to-many via post_targets)
post_targets (post_id, social_account_id)
Security Architecture¶
Authentication Flow¶
1. User submits credentials
└→ POST /api/v1/auth/login
2. Server validates credentials
└→ Check password hash
└→ Check account status
3. Issue tokens
└→ Access token (JWT, 15min)
└→ Refresh token (7 days, httpOnly cookie)
4. Client stores access token
└→ Authorization: Bearer <token>
5. Token refresh (when expired)
└→ POST /api/v1/auth/refresh
Permission System (RBAC)¶
30+ granular permissions:
| Category | Permissions |
|---|---|
| Posts | posts.create, posts.edit, posts.delete, posts.publish |
| Brands | brands.create, brands.edit, brands.delete |
| Accounts | accounts.connect, accounts.disconnect |
| Team | users.invite, users.manage, roles.manage |
| Analytics | analytics.view, analytics.export |
| Admin | workspace.settings, billing.manage |
Token Encryption¶
OAuth tokens are encrypted at rest using AES-256-GCM:
// Encryption (before storage)
encrypt(accessToken) → base64(iv + ciphertext + authTag)
// Decryption (when needed)
decrypt(encryptedToken) → plainAccessToken
White-Label Architecture¶
Custom Domain Flow¶
1. Client configures DNS
client.example.com → CNAME → mypost.your-server.com
2. Add domain in MyPost
POST /api/v1/workspaces/:id/custom-domain
3. Domain verification
└→ DNS TXT record check
└→ SSL certificate provisioning
4. Request routing
└→ Domain → workspace_id lookup
└→ X-Workspace-ID header injection
Branding Customization¶
| Element | Customizable |
|---|---|
| Logo | ✅ |
| Colors | ✅ |
| Login page | ✅ |
| Email templates | ✅ |
| Footer text | ✅ |
| Favicon | ✅ |
Network Adapters¶
Each social network has a dedicated adapter implementing a common interface:
interface NetworkAdapter {
// Authentication
getAuthUrl(): string;
exchangeCode(code: string): Promise<TokenSet>;
refreshToken(token: TokenSet): Promise<TokenSet>;
// Publishing
publish(post: Post, account: SocialAccount): Promise<PublishResult>;
// Analytics
getMetrics(accountId: string, range: DateRange): Promise<Metrics>;
}
Supported Networks¶
| Network | Features |
|---|---|
| Pages, Groups, Stories | |
| Feed, Stories, Reels | |
| X (Twitter) | Tweets, Threads, Media |
| Posts, Articles, Pages | |
| TikTok | Videos |
| YouTube | Videos, Community, Shorts |
Caching Strategy¶
Redis Cache Layers¶
| Key Pattern | TTL | Purpose |
|---|---|---|
user:{id} |
1h | User profile data |
workspace:{id} |
1h | Workspace config |
domain:{domain} |
1h | Domain → workspace mapping |
session:{token} |
15m | Active sessions |
rate:{key} |
1s-1m | Rate limiting counters |
Cache Invalidation¶
// On entity update
await cache.del(`workspace:${id}`);
// On domain change
await cache.del(`domain:${oldDomain}`);
await cache.set(`domain:${newDomain}`, workspaceId);
Scaling Considerations¶
Horizontal Scaling¶
| Component | Scalable | Notes |
|---|---|---|
| Web | ✅ | Stateless, add replicas |
| API | ✅ | Stateless, add replicas |
| Worker | ✅ | Scale based on queue depth |
| PostgreSQL | ⚠️ | Use read replicas |
| Redis | ⚠️ | Cluster mode for HA |
Recommended Production Setup¶
| Load | API Replicas | Workers | PostgreSQL |
|---|---|---|---|
| Small (<100 users) | 1 | 1 | 1 primary |
| Medium (<1000 users) | 2-3 | 2 | 1 primary + 1 replica |
| Large (1000+ users) | 4+ | 4+ | Primary + replicas + PgBouncer |
For More Details¶
- Deployment Guide — Production configuration
- Operations Guide — Backup, monitoring, maintenance
- API Reference — Complete endpoint documentation