first commit
This commit is contained in:
828
Overview/README.md
Normal file
828
Overview/README.md
Normal file
@@ -0,0 +1,828 @@
|
||||
# MeetVault
|
||||
|
||||
MeetVault is a self-hosted meeting recording and intelligence platform.
|
||||
|
||||
The user machine handles meeting recording and local transcription. The backend handles authentication, meeting metadata, transcript ingestion, summarization jobs, task extraction, reminder scheduling, and persistent storage.
|
||||
|
||||
Large media files are stored in SeaweedFS through its S3-compatible API, while PostgreSQL stores structured application data.
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### User Machine
|
||||
|
||||
The browser or desktop application is responsible for:
|
||||
|
||||
- Recording meeting audio/video
|
||||
- Running local speech-to-text using Whisper, whisper.cpp, or another local STT engine
|
||||
- Sending the generated transcript to the backend
|
||||
- Uploading large media files directly to SeaweedFS using presigned URLs
|
||||
|
||||
Local transcription keeps transcription compute and cost on the user's device rather than on the backend.
|
||||
|
||||
### Backend Infrastructure
|
||||
|
||||
The backend API is responsible for:
|
||||
|
||||
- Authentication
|
||||
- User management
|
||||
- Meeting management
|
||||
- Presigned upload URL generation
|
||||
- Transcript ingestion
|
||||
- Summarization job creation
|
||||
- Action-item extraction
|
||||
- Reminder scheduling
|
||||
- Job status tracking
|
||||
|
||||
PostgreSQL stores structured application data such as users, meetings, transcripts, summaries, tasks, schedules, and processing jobs.
|
||||
|
||||
SeaweedFS stores the actual large binary files such as:
|
||||
|
||||
- Video
|
||||
- Audio
|
||||
- Thumbnails
|
||||
- Attachments
|
||||
- Exported files
|
||||
|
||||
---
|
||||
|
||||
## Data Flow
|
||||
|
||||
```text
|
||||
User machine
|
||||
│
|
||||
├── video ─────────────────────────────► SeaweedFS
|
||||
│
|
||||
└── local transcript ────────────────► PostgreSQL
|
||||
│
|
||||
▼
|
||||
processing_jobs
|
||||
│
|
||||
Summarizer
|
||||
│
|
||||
┌─────────────┴─────────────┐
|
||||
▼ ▼
|
||||
meeting_tasks meeting_summaries
|
||||
│
|
||||
▼
|
||||
meeting_schedules
|
||||
│
|
||||
▼
|
||||
Reminder Worker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Database Design
|
||||
|
||||
```text
|
||||
users
|
||||
│
|
||||
├──── auth_identities
|
||||
│
|
||||
├──── subscriptions
|
||||
│
|
||||
├──── meetings
|
||||
│ │
|
||||
│ ├──── meeting_files ─────────► SeaweedFS
|
||||
│ │
|
||||
│ ├──── transcripts
|
||||
│ │
|
||||
│ ├──── meeting_summaries
|
||||
│ │
|
||||
│ ├──── meeting_tasks
|
||||
│ │ │
|
||||
│ │ └──── meeting_schedules
|
||||
│ │
|
||||
│ └──── processing_jobs
|
||||
│
|
||||
└──── meeting_schedules
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# PostgreSQL Schema
|
||||
|
||||
## Enable UUID Generation
|
||||
|
||||
```sql
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Users
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
email VARCHAR(320) UNIQUE NOT NULL,
|
||||
display_name VARCHAR(255),
|
||||
|
||||
identification_status VARCHAR(30)
|
||||
NOT NULL DEFAULT 'unidentified'
|
||||
CHECK (
|
||||
identification_status IN (
|
||||
'unidentified',
|
||||
'pending',
|
||||
'identified',
|
||||
'rejected'
|
||||
)
|
||||
),
|
||||
|
||||
account_status VARCHAR(30)
|
||||
NOT NULL DEFAULT 'active'
|
||||
CHECK (
|
||||
account_status IN (
|
||||
'active',
|
||||
'suspended',
|
||||
'disabled'
|
||||
)
|
||||
),
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentication Identities
|
||||
|
||||
Supports local login and external providers such as Google or Microsoft.
|
||||
|
||||
```sql
|
||||
CREATE TABLE auth_identities (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
user_id UUID NOT NULL
|
||||
REFERENCES users(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
provider VARCHAR(50) NOT NULL,
|
||||
|
||||
provider_user_id VARCHAR(255),
|
||||
password_hash TEXT,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
UNIQUE(provider, provider_user_id)
|
||||
);
|
||||
```
|
||||
|
||||
Typical providers:
|
||||
|
||||
```text
|
||||
local
|
||||
google
|
||||
microsoft
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Subscriptions
|
||||
|
||||
```sql
|
||||
CREATE TABLE subscriptions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
user_id UUID NOT NULL
|
||||
REFERENCES users(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
plan VARCHAR(50)
|
||||
NOT NULL DEFAULT 'free',
|
||||
|
||||
status VARCHAR(30)
|
||||
NOT NULL DEFAULT 'active'
|
||||
CHECK (
|
||||
status IN (
|
||||
'active',
|
||||
'trialing',
|
||||
'past_due',
|
||||
'cancelled',
|
||||
'expired'
|
||||
)
|
||||
),
|
||||
|
||||
provider VARCHAR(50),
|
||||
|
||||
provider_customer_id VARCHAR(255),
|
||||
provider_subscription_id VARCHAR(255),
|
||||
|
||||
current_period_start TIMESTAMPTZ,
|
||||
current_period_end TIMESTAMPTZ,
|
||||
|
||||
cancel_at_period_end BOOLEAN
|
||||
NOT NULL DEFAULT FALSE,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Meetings
|
||||
|
||||
```sql
|
||||
CREATE TABLE meetings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
owner_user_id UUID NOT NULL
|
||||
REFERENCES users(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
title VARCHAR(500),
|
||||
description TEXT,
|
||||
|
||||
started_at TIMESTAMPTZ,
|
||||
ended_at TIMESTAMPTZ,
|
||||
|
||||
timezone VARCHAR(100),
|
||||
|
||||
status VARCHAR(30)
|
||||
NOT NULL DEFAULT 'created'
|
||||
CHECK (
|
||||
status IN (
|
||||
'created',
|
||||
'recording',
|
||||
'uploading',
|
||||
'transcribing',
|
||||
'processing',
|
||||
'ready',
|
||||
'failed',
|
||||
'archived',
|
||||
'deleted'
|
||||
)
|
||||
),
|
||||
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
|
||||
deleted_at TIMESTAMPTZ,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
CHECK (
|
||||
ended_at IS NULL
|
||||
OR started_at IS NULL
|
||||
OR ended_at >= started_at
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
Summaries are stored separately in `meeting_summaries`.
|
||||
|
||||
---
|
||||
|
||||
## Meeting Files
|
||||
|
||||
This table stores file metadata only. The actual media files are stored in SeaweedFS.
|
||||
|
||||
```sql
|
||||
CREATE TABLE meeting_files (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
meeting_id UUID NOT NULL
|
||||
REFERENCES meetings(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
uploaded_by UUID
|
||||
REFERENCES users(id)
|
||||
ON DELETE SET NULL,
|
||||
|
||||
file_type VARCHAR(50)
|
||||
NOT NULL
|
||||
CHECK (
|
||||
file_type IN (
|
||||
'video',
|
||||
'audio',
|
||||
'thumbnail',
|
||||
'attachment',
|
||||
'export'
|
||||
)
|
||||
),
|
||||
|
||||
original_filename VARCHAR(500),
|
||||
mime_type VARCHAR(150),
|
||||
|
||||
storage_provider VARCHAR(30)
|
||||
NOT NULL DEFAULT 'seaweedfs',
|
||||
|
||||
storage_bucket VARCHAR(255) NOT NULL,
|
||||
storage_key TEXT NOT NULL,
|
||||
|
||||
size_bytes BIGINT
|
||||
CHECK (
|
||||
size_bytes IS NULL
|
||||
OR size_bytes >= 0
|
||||
),
|
||||
|
||||
checksum VARCHAR(255),
|
||||
|
||||
upload_status VARCHAR(30)
|
||||
NOT NULL DEFAULT 'pending'
|
||||
CHECK (
|
||||
upload_status IN (
|
||||
'pending',
|
||||
'uploading',
|
||||
'uploaded',
|
||||
'failed',
|
||||
'deleted'
|
||||
)
|
||||
),
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
UNIQUE(storage_provider, storage_bucket, storage_key)
|
||||
);
|
||||
```
|
||||
|
||||
Example SeaweedFS object path:
|
||||
|
||||
```text
|
||||
meeting-storage/
|
||||
users/{user_id}/
|
||||
meetings/{meeting_id}/
|
||||
original/
|
||||
recording.webm
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Transcripts
|
||||
|
||||
Transcription happens locally on the user's machine and is uploaded to the backend afterward.
|
||||
|
||||
```sql
|
||||
CREATE TABLE transcripts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
meeting_id UUID NOT NULL
|
||||
REFERENCES meetings(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
source VARCHAR(30)
|
||||
NOT NULL DEFAULT 'local'
|
||||
CHECK (
|
||||
source IN (
|
||||
'local',
|
||||
'imported'
|
||||
)
|
||||
),
|
||||
|
||||
engine VARCHAR(100),
|
||||
language VARCHAR(20),
|
||||
|
||||
version INTEGER
|
||||
NOT NULL DEFAULT 1
|
||||
CHECK (version > 0),
|
||||
|
||||
is_active BOOLEAN
|
||||
NOT NULL DEFAULT TRUE,
|
||||
|
||||
plain_text TEXT,
|
||||
transcript_json JSONB,
|
||||
|
||||
duration_seconds NUMERIC(12,2)
|
||||
CHECK (
|
||||
duration_seconds IS NULL
|
||||
OR duration_seconds >= 0
|
||||
),
|
||||
|
||||
status VARCHAR(30)
|
||||
NOT NULL DEFAULT 'uploaded'
|
||||
CHECK (
|
||||
status IN (
|
||||
'uploaded',
|
||||
'processing',
|
||||
'ready',
|
||||
'failed'
|
||||
)
|
||||
),
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
UNIQUE(meeting_id, version)
|
||||
);
|
||||
```
|
||||
|
||||
Example transcript JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"segments": [
|
||||
{
|
||||
"speaker": "Speaker 1",
|
||||
"start": 4.2,
|
||||
"end": 9.8,
|
||||
"text": "Let's review the deployment."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Meeting Summaries
|
||||
|
||||
```sql
|
||||
CREATE TABLE meeting_summaries (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
meeting_id UUID NOT NULL
|
||||
REFERENCES meetings(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
summary_type VARCHAR(50)
|
||||
NOT NULL DEFAULT 'general',
|
||||
|
||||
content TEXT NOT NULL,
|
||||
|
||||
model VARCHAR(100),
|
||||
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
Possible summary types:
|
||||
|
||||
```text
|
||||
general
|
||||
short
|
||||
executive
|
||||
technical
|
||||
action-focused
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Meeting Tasks
|
||||
|
||||
```sql
|
||||
CREATE TABLE meeting_tasks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
meeting_id UUID NOT NULL
|
||||
REFERENCES meetings(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
assigned_user_id UUID
|
||||
REFERENCES users(id)
|
||||
ON DELETE SET NULL,
|
||||
|
||||
title VARCHAR(500) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
due_at TIMESTAMPTZ,
|
||||
|
||||
status VARCHAR(30)
|
||||
NOT NULL DEFAULT 'open'
|
||||
CHECK (
|
||||
status IN (
|
||||
'open',
|
||||
'in_progress',
|
||||
'completed',
|
||||
'cancelled'
|
||||
)
|
||||
),
|
||||
|
||||
source VARCHAR(30)
|
||||
NOT NULL DEFAULT 'ai'
|
||||
CHECK (
|
||||
source IN (
|
||||
'ai',
|
||||
'manual',
|
||||
'imported'
|
||||
)
|
||||
),
|
||||
|
||||
source_metadata JSONB,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Meeting Schedules / Reminders
|
||||
|
||||
```sql
|
||||
CREATE TABLE meeting_schedules (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
meeting_id UUID NOT NULL
|
||||
REFERENCES meetings(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
task_id UUID
|
||||
REFERENCES meeting_tasks(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
user_id UUID NOT NULL
|
||||
REFERENCES users(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
schedule_type VARCHAR(50) NOT NULL,
|
||||
|
||||
scheduled_at TIMESTAMPTZ NOT NULL,
|
||||
|
||||
status VARCHAR(30)
|
||||
NOT NULL DEFAULT 'pending'
|
||||
CHECK (
|
||||
status IN (
|
||||
'pending',
|
||||
'processing',
|
||||
'executed',
|
||||
'failed',
|
||||
'cancelled'
|
||||
)
|
||||
),
|
||||
|
||||
payload JSONB NOT NULL DEFAULT '{}',
|
||||
|
||||
executed_at TIMESTAMPTZ,
|
||||
|
||||
attempts INTEGER
|
||||
NOT NULL DEFAULT 0,
|
||||
|
||||
last_error TEXT,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
Example reminder payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"channel": "email",
|
||||
"message": "Prepare the deployment report"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Processing Jobs
|
||||
|
||||
MeetVault can use PostgreSQL itself as the first job queue.
|
||||
|
||||
```sql
|
||||
CREATE TABLE processing_jobs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
meeting_id UUID
|
||||
REFERENCES meetings(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
job_type VARCHAR(50)
|
||||
NOT NULL
|
||||
CHECK (
|
||||
job_type IN (
|
||||
'summarize_meeting',
|
||||
'extract_action_items',
|
||||
'generate_reminders'
|
||||
)
|
||||
),
|
||||
|
||||
status VARCHAR(30)
|
||||
NOT NULL DEFAULT 'queued'
|
||||
CHECK (
|
||||
status IN (
|
||||
'queued',
|
||||
'running',
|
||||
'completed',
|
||||
'failed',
|
||||
'cancelled'
|
||||
)
|
||||
),
|
||||
|
||||
priority SMALLINT
|
||||
NOT NULL DEFAULT 0,
|
||||
|
||||
attempts INTEGER
|
||||
NOT NULL DEFAULT 0,
|
||||
|
||||
max_attempts INTEGER
|
||||
NOT NULL DEFAULT 3
|
||||
CHECK (max_attempts > 0),
|
||||
|
||||
input_data JSONB,
|
||||
output_data JSONB,
|
||||
|
||||
error_message TEXT,
|
||||
|
||||
available_at TIMESTAMPTZ
|
||||
NOT NULL DEFAULT NOW(),
|
||||
|
||||
locked_at TIMESTAMPTZ,
|
||||
locked_by VARCHAR(255),
|
||||
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
Example job pickup query:
|
||||
|
||||
```sql
|
||||
SELECT *
|
||||
FROM processing_jobs
|
||||
WHERE status = 'queued'
|
||||
AND available_at <= NOW()
|
||||
ORDER BY priority DESC, created_at
|
||||
FOR UPDATE SKIP LOCKED
|
||||
LIMIT 1;
|
||||
```
|
||||
|
||||
This allows multiple workers without two workers processing the same job.
|
||||
|
||||
Redis or another dedicated queue can be added later if required.
|
||||
|
||||
---
|
||||
|
||||
# Indexes
|
||||
|
||||
```sql
|
||||
CREATE INDEX idx_meetings_owner
|
||||
ON meetings(owner_user_id);
|
||||
|
||||
CREATE INDEX idx_meetings_owner_started
|
||||
ON meetings(owner_user_id, started_at DESC);
|
||||
|
||||
CREATE INDEX idx_meetings_status
|
||||
ON meetings(status);
|
||||
|
||||
CREATE INDEX idx_meeting_files_meeting
|
||||
ON meeting_files(meeting_id);
|
||||
|
||||
CREATE INDEX idx_transcripts_meeting
|
||||
ON transcripts(meeting_id);
|
||||
|
||||
CREATE INDEX idx_transcripts_active
|
||||
ON transcripts(meeting_id, is_active);
|
||||
|
||||
CREATE INDEX idx_summaries_meeting
|
||||
ON meeting_summaries(meeting_id);
|
||||
|
||||
CREATE INDEX idx_tasks_meeting
|
||||
ON meeting_tasks(meeting_id);
|
||||
|
||||
CREATE INDEX idx_tasks_assigned_user
|
||||
ON meeting_tasks(assigned_user_id);
|
||||
|
||||
CREATE INDEX idx_schedules_due
|
||||
ON meeting_schedules(status, scheduled_at);
|
||||
|
||||
CREATE INDEX idx_processing_jobs_queue
|
||||
ON processing_jobs(status, available_at, priority DESC);
|
||||
```
|
||||
|
||||
The most important worker indexes are:
|
||||
|
||||
```text
|
||||
meeting_schedules(status, scheduled_at)
|
||||
processing_jobs(status, available_at, priority)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Automatic `updated_at`
|
||||
|
||||
PostgreSQL does not automatically modify `updated_at` when a row changes.
|
||||
|
||||
Create a shared trigger function:
|
||||
|
||||
```sql
|
||||
CREATE OR REPLACE FUNCTION set_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
```
|
||||
|
||||
Apply it to relevant tables:
|
||||
|
||||
```sql
|
||||
CREATE TRIGGER trg_users_updated_at
|
||||
BEFORE UPDATE ON users
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_subscriptions_updated_at
|
||||
BEFORE UPDATE ON subscriptions
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_meetings_updated_at
|
||||
BEFORE UPDATE ON meetings
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_transcripts_updated_at
|
||||
BEFORE UPDATE ON transcripts
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_tasks_updated_at
|
||||
BEFORE UPDATE ON meeting_tasks
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_schedules_updated_at
|
||||
BEFORE UPDATE ON meeting_schedules
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION set_updated_at();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Final Architecture
|
||||
|
||||
```text
|
||||
MeetVault
|
||||
|
||||
USER MACHINE
|
||||
│
|
||||
┌──────────┴──────────┐
|
||||
│ │
|
||||
Recording Local STT
|
||||
│ Whisper
|
||||
│ │
|
||||
▼ ▼
|
||||
Video/audio Transcript
|
||||
│ │
|
||||
│ ▼
|
||||
│ Backend API
|
||||
│ │
|
||||
│ ▼
|
||||
│ PostgreSQL
|
||||
│ │
|
||||
│ processing_jobs
|
||||
│ │
|
||||
│ ▼
|
||||
│ Summary Worker
|
||||
│ / \
|
||||
│ / \
|
||||
▼ ▼ ▼
|
||||
SeaweedFS meeting_tasks summaries
|
||||
│
|
||||
▼
|
||||
meeting_schedules
|
||||
│
|
||||
▼
|
||||
Reminder Worker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Recommended MVP Stack
|
||||
|
||||
A practical first implementation could use:
|
||||
|
||||
```text
|
||||
Frontend
|
||||
React / Next.js
|
||||
|
||||
Backend
|
||||
FastAPI or Node.js
|
||||
|
||||
Database
|
||||
PostgreSQL
|
||||
|
||||
Media Storage
|
||||
SeaweedFS
|
||||
|
||||
Local Transcription
|
||||
Whisper / whisper.cpp
|
||||
|
||||
Backend Jobs
|
||||
PostgreSQL processing_jobs
|
||||
|
||||
Workers
|
||||
Summarization Worker
|
||||
Reminder Worker
|
||||
```
|
||||
|
||||
For the first version, MeetVault does not require:
|
||||
|
||||
- Kafka
|
||||
- Elasticsearch
|
||||
- Kubernetes
|
||||
- A vector database
|
||||
- A separate microservice architecture
|
||||
- Redis, unless job volume grows enough to justify it
|
||||
|
||||
PostgreSQL + SeaweedFS + the backend API + workers is sufficient for the MVP and leaves room to scale later.
|
||||
Reference in New Issue
Block a user