Skip to main content
Glama
DATABASE_DESIGN.mdโ€ข18.8 kB
# ๐Ÿ—„๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„ - WorkflowMCP Dashboard API ## ๐ŸŽฏ ์„ค๊ณ„ ์›์น™ 1. **๊ธฐ์กด ์Šคํ‚ค๋งˆ ๋ณด์กด**: ํ˜„์žฌ SQLite ์Šคํ‚ค๋งˆ ์ˆ˜์ • ์—†์ด ํ™•์žฅ๋งŒ 2. **API ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ง€์›**: Help, Schema, Example ๋ฐ์ดํ„ฐ ์ €์žฅ 3. **์„ฑ๋Šฅ ์ตœ์ ํ™”**: API ์‘๋‹ต ์†๋„ ํ–ฅ์ƒ์„ ์œ„ํ•œ ์ธ๋ฑ์Šค ์ถ”๊ฐ€ 4. **ํ™•์žฅ์„ฑ**: ํ–ฅํ›„ ๊ธฐ๋Šฅ ์ถ”๊ฐ€๋ฅผ ์œ„ํ•œ ์œ ์—ฐํ•œ ๊ตฌ์กฐ ## ๐Ÿ“Š ๊ธฐ์กด ์Šคํ‚ค๋งˆ ๋ถ„์„ ### ํ˜„์žฌ ํ•ต์‹ฌ ํ…Œ์ด๋ธ” (์œ ์ง€) ```sql -- ๊ธฐ์กด ํ…Œ์ด๋ธ”๋“ค (์ˆ˜์ • ์—†์Œ) prds (id, title, description, requirements, acceptance_criteria, priority, status, project_id, created_at, updated_at) tasks (id, title, description, status, priority, assignee, due_date, estimated_hours, actual_hours, tags, project_id, created_at, updated_at) documents (id, title, content, doc_type, category, summary, tags, status, project_id, created_at, updated_at) document_links (id, document_id, entity_type, entity_id, link_type, created_at) task_dependencies (id, task_id, depends_on_task_id, dependency_type, created_at) -- FTS ํ…Œ์ด๋ธ” (์œ ์ง€) documents_fts (document_id, title, content, category, tags) ``` ### ๊ธฐ์กด ์ธ๋ฑ์Šค (์œ ์ง€) ```sql -- ์„ฑ๋Šฅ ์ธ๋ฑ์Šค๋“ค (๊ธฐ์กด) CREATE INDEX idx_prds_status ON prds(status); CREATE INDEX idx_prds_project_id ON prds(project_id); CREATE INDEX idx_tasks_status ON tasks(status); CREATE INDEX idx_tasks_assignee ON tasks(assignee); CREATE INDEX idx_documents_type ON documents(doc_type); CREATE INDEX idx_documents_category ON documents(category); ``` ## ๐Ÿ†• API ํ™•์žฅ ํ…Œ์ด๋ธ” ### 1. API ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ” #### api_schemas - API ์Šคํ‚ค๋งˆ ์ •๋ณด ```sql CREATE TABLE api_schemas ( id INTEGER PRIMARY KEY AUTOINCREMENT, endpoint VARCHAR(255) NOT NULL UNIQUE, -- '/api/prds', '/api/tasks' ๋“ฑ method VARCHAR(10) NOT NULL, -- 'GET', 'POST', 'PUT', 'DELETE' category VARCHAR(100) NOT NULL, -- 'prds', 'tasks', 'documents' ๋“ฑ input_schema TEXT, -- JSON Schema for request body output_schema TEXT, -- JSON Schema for response description TEXT, -- API ์—”๋“œํฌ์ธํŠธ ์„ค๋ช… deprecated BOOLEAN DEFAULT FALSE, -- ํ๊ธฐ ์˜ˆ์ • ์—ฌ๋ถ€ version VARCHAR(20) DEFAULT 'v1', -- API ๋ฒ„์ „ created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- ์ธ๋ฑ์Šค CREATE INDEX idx_api_schemas_endpoint ON api_schemas(endpoint); CREATE INDEX idx_api_schemas_category ON api_schemas(category); CREATE INDEX idx_api_schemas_method ON api_schemas(method); ``` #### api_examples - API ์‚ฌ์šฉ ์˜ˆ์ œ ```sql CREATE TABLE api_examples ( id INTEGER PRIMARY KEY AUTOINCREMENT, schema_id INTEGER NOT NULL, -- api_schemas.id ์ฐธ์กฐ name VARCHAR(255) NOT NULL, -- ์˜ˆ์ œ ์ด๋ฆ„ description TEXT, -- ์˜ˆ์ œ ์„ค๋ช… request_example TEXT, -- ์š”์ฒญ ์˜ˆ์ œ (JSON) response_example TEXT, -- ์‘๋‹ต ์˜ˆ์ œ (JSON) curl_example TEXT, -- cURL ๋ช…๋ น์–ด ์˜ˆ์ œ scenario VARCHAR(100), -- 'basic', 'advanced', 'error' ๋“ฑ order_index INTEGER DEFAULT 0, -- ํ‘œ์‹œ ์ˆœ์„œ created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (schema_id) REFERENCES api_schemas(id) ON DELETE CASCADE ); -- ์ธ๋ฑ์Šค CREATE INDEX idx_api_examples_schema_id ON api_examples(schema_id); CREATE INDEX idx_api_examples_scenario ON api_examples(scenario); ``` #### api_help_content - Help ์ปจํ…์ธ  ```sql CREATE TABLE api_help_content ( id INTEGER PRIMARY KEY AUTOINCREMENT, path VARCHAR(255) NOT NULL UNIQUE, -- '/help/prds/overview' ๋“ฑ title VARCHAR(255) NOT NULL, -- ๋„์›€๋ง ์ œ๋ชฉ content TEXT NOT NULL, -- ๋„์›€๋ง ๋‚ด์šฉ (Markdown) category VARCHAR(100) NOT NULL, -- 'prds', 'tasks', 'getting-started' ๋“ฑ subcategory VARCHAR(100), -- 'overview', 'examples', 'troubleshooting' ๋“ฑ tags TEXT, -- ๊ฒ€์ƒ‰์šฉ ํƒœ๊ทธ (JSON array) order_index INTEGER DEFAULT 0, -- ํ‘œ์‹œ ์ˆœ์„œ parent_path VARCHAR(255), -- ์ƒ์œ„ ๋„์›€๋ง ๊ฒฝ๋กœ version VARCHAR(20) DEFAULT 'v1', -- ๋„์›€๋ง ๋ฒ„์ „ created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- ์ธ๋ฑ์Šค CREATE INDEX idx_api_help_path ON api_help_content(path); CREATE INDEX idx_api_help_category ON api_help_content(category); CREATE INDEX idx_api_help_parent ON api_help_content(parent_path); -- FTS for help content search CREATE VIRTUAL TABLE api_help_fts USING fts5( help_id UNINDEXED, title, content, category, tags, content='api_help_content', content_rowid='id' ); -- FTS ํŠธ๋ฆฌ๊ฑฐ CREATE TRIGGER api_help_fts_insert AFTER INSERT ON api_help_content BEGIN INSERT INTO api_help_fts(help_id, title, content, category, tags) VALUES (new.id, new.title, new.content, new.category, new.tags); END; CREATE TRIGGER api_help_fts_update AFTER UPDATE ON api_help_content BEGIN UPDATE api_help_fts SET title = new.title, content = new.content, category = new.category, tags = new.tags WHERE help_id = new.id; END; CREATE TRIGGER api_help_fts_delete AFTER DELETE ON api_help_content BEGIN DELETE FROM api_help_fts WHERE help_id = old.id; END; ``` ### 2. API ์‚ฌ์šฉ ํ†ต๊ณ„ ํ…Œ์ด๋ธ” #### api_usage_stats - API ์‚ฌ์šฉ ํ†ต๊ณ„ ```sql CREATE TABLE api_usage_stats ( id INTEGER PRIMARY KEY AUTOINCREMENT, endpoint VARCHAR(255) NOT NULL, -- API ์—”๋“œํฌ์ธํŠธ method VARCHAR(10) NOT NULL, -- HTTP ๋ฉ”์„œ๋“œ status_code INTEGER NOT NULL, -- HTTP ์ƒํƒœ ์ฝ”๋“œ response_time_ms INTEGER, -- ์‘๋‹ต ์‹œ๊ฐ„ (๋ฐ€๋ฆฌ์ดˆ) request_size INTEGER, -- ์š”์ฒญ ํฌ๊ธฐ (bytes) response_size INTEGER, -- ์‘๋‹ต ํฌ๊ธฐ (bytes) user_agent TEXT, -- User-Agent session_id VARCHAR(100), -- ์„ธ์…˜ ID error_message TEXT, -- ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ (์‹คํŒจ ์‹œ) created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- ์ธ๋ฑ์Šค (์„ฑ๋Šฅ ๋ถ„์„์šฉ) CREATE INDEX idx_api_usage_endpoint ON api_usage_stats(endpoint); CREATE INDEX idx_api_usage_status ON api_usage_stats(status_code); CREATE INDEX idx_api_usage_created ON api_usage_stats(created_at); CREATE INDEX idx_api_usage_session ON api_usage_stats(session_id); ``` #### api_rate_limits - Rate Limiting ```sql CREATE TABLE api_rate_limits ( id INTEGER PRIMARY KEY AUTOINCREMENT, identifier VARCHAR(255) NOT NULL, -- IP ์ฃผ์†Œ ๋˜๋Š” API ํ‚ค endpoint VARCHAR(255), -- ํŠน์ • ์—”๋“œํฌ์ธํŠธ (NULL์ด๋ฉด ์ „์ฒด) request_count INTEGER DEFAULT 0, -- ํ˜„์žฌ ์š”์ฒญ ์ˆ˜ window_start DATETIME NOT NULL, -- ์œˆ๋„์šฐ ์‹œ์ž‘ ์‹œ๊ฐ„ window_duration INTEGER DEFAULT 900, -- ์œˆ๋„์šฐ ์ง€์†์‹œ๊ฐ„ (์ดˆ, ๊ธฐ๋ณธ 15๋ถ„) max_requests INTEGER DEFAULT 1000, -- ์ตœ๋Œ€ ์š”์ฒญ ์ˆ˜ created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE(identifier, endpoint, window_start) ); -- ์ธ๋ฑ์Šค CREATE INDEX idx_rate_limits_identifier ON api_rate_limits(identifier); CREATE INDEX idx_rate_limits_window ON api_rate_limits(window_start); ``` ### 3. ์‹ค์‹œ๊ฐ„ ํ†ต์‹  ํ…Œ์ด๋ธ” #### sse_connections - SSE ์—ฐ๊ฒฐ ๊ด€๋ฆฌ ```sql CREATE TABLE sse_connections ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id VARCHAR(100) NOT NULL UNIQUE, -- ์„ธ์…˜ ID connection_id VARCHAR(100) NOT NULL, -- ์—ฐ๊ฒฐ ID filters TEXT, -- ์ด๋ฒคํŠธ ํ•„ํ„ฐ (JSON) connected_at DATETIME DEFAULT CURRENT_TIMESTAMP, last_ping DATETIME DEFAULT CURRENT_TIMESTAMP, is_active BOOLEAN DEFAULT TRUE ); -- ์ธ๋ฑ์Šค CREATE INDEX idx_sse_session ON sse_connections(session_id); CREATE INDEX idx_sse_active ON sse_connections(is_active); ``` #### sse_events - SSE ์ด๋ฒคํŠธ ํ ```sql CREATE TABLE sse_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, event_type VARCHAR(100) NOT NULL, -- 'prd:created', 'task:updated' ๋“ฑ entity_type VARCHAR(50) NOT NULL, -- 'prd', 'task', 'document' entity_id VARCHAR(100) NOT NULL, -- ์—”ํ‹ฐํ‹ฐ ID event_data TEXT, -- ์ด๋ฒคํŠธ ๋ฐ์ดํ„ฐ (JSON) session_filters TEXT, -- ๋Œ€์ƒ ์„ธ์…˜ ํ•„ํ„ฐ (JSON array) created_at DATETIME DEFAULT CURRENT_TIMESTAMP, processed_at DATETIME, -- ์ฒ˜๋ฆฌ ์™„๋ฃŒ ์‹œ๊ฐ„ is_processed BOOLEAN DEFAULT FALSE ); -- ์ธ๋ฑ์Šค CREATE INDEX idx_sse_events_type ON sse_events(event_type); CREATE INDEX idx_sse_events_processed ON sse_events(is_processed); CREATE INDEX idx_sse_events_created ON sse_events(created_at); ``` ### 4. API ์บ์‹œ ํ…Œ์ด๋ธ” #### api_cache - API ์‘๋‹ต ์บ์‹œ ```sql CREATE TABLE api_cache ( id INTEGER PRIMARY KEY AUTOINCREMENT, cache_key VARCHAR(255) NOT NULL UNIQUE, -- ์บ์‹œ ํ‚ค (endpoint + params hash) endpoint VARCHAR(255) NOT NULL, -- API ์—”๋“œํฌ์ธํŠธ params_hash VARCHAR(64), -- ํŒŒ๋ผ๋ฏธํ„ฐ ํ•ด์‹œ๊ฐ’ response_data TEXT NOT NULL, -- ์บ์‹œ๋œ ์‘๋‹ต (JSON) content_type VARCHAR(100) DEFAULT 'application/json', expires_at DATETIME NOT NULL, -- ๋งŒ๋ฃŒ ์‹œ๊ฐ„ hit_count INTEGER DEFAULT 0, -- ํžˆํŠธ ํšŸ์ˆ˜ created_at DATETIME DEFAULT CURRENT_TIMESTAMP, last_accessed DATETIME DEFAULT CURRENT_TIMESTAMP ); -- ์ธ๋ฑ์Šค CREATE INDEX idx_api_cache_key ON api_cache(cache_key); CREATE INDEX idx_api_cache_expires ON api_cache(expires_at); CREATE INDEX idx_api_cache_endpoint ON api_cache(endpoint); ``` ## ๐Ÿ”ง ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ธ๋ฑ์Šค ### 1. API ์กฐํšŒ ์„ฑ๋Šฅ ํ–ฅ์ƒ ์ธ๋ฑ์Šค ```sql -- PRD API ์„ฑ๋Šฅ ์ธ๋ฑ์Šค CREATE INDEX idx_prds_status_project ON prds(status, project_id); CREATE INDEX idx_prds_created_desc ON prds(created_at DESC); CREATE INDEX idx_prds_updated_desc ON prds(updated_at DESC); -- Task API ์„ฑ๋Šฅ ์ธ๋ฑ์Šค CREATE INDEX idx_tasks_status_priority ON tasks(status, priority); CREATE INDEX idx_tasks_assignee_status ON tasks(assignee, status); CREATE INDEX idx_tasks_due_date ON tasks(due_date); CREATE INDEX idx_tasks_project_status ON tasks(project_id, status); -- Document API ์„ฑ๋Šฅ ์ธ๋ฑ์Šค CREATE INDEX idx_documents_type_status ON documents(doc_type, status); CREATE INDEX idx_documents_category_type ON documents(category, doc_type); CREATE INDEX idx_documents_project_type ON documents(project_id, doc_type); -- ์—ฐ๊ฒฐ ๊ด€๊ณ„ ์„ฑ๋Šฅ ์ธ๋ฑ์Šค CREATE INDEX idx_document_links_entity ON document_links(entity_type, entity_id); CREATE INDEX idx_document_links_document ON document_links(document_id, link_type); CREATE INDEX idx_task_deps_task ON task_dependencies(task_id); CREATE INDEX idx_task_deps_depends ON task_dependencies(depends_on_task_id); ``` ### 2. ๋ณตํ•ฉ ์ฟผ๋ฆฌ ์ตœ์ ํ™” ์ธ๋ฑ์Šค ```sql -- ๋Œ€์‹œ๋ณด๋“œ ์ฟผ๋ฆฌ ์ตœ์ ํ™” CREATE INDEX idx_prds_project_status_created ON prds(project_id, status, created_at DESC); CREATE INDEX idx_tasks_project_status_created ON tasks(project_id, status, created_at DESC); CREATE INDEX idx_documents_project_type_created ON documents(project_id, doc_type, created_at DESC); -- ํ†ต๊ณ„ ์ฟผ๋ฆฌ ์ตœ์ ํ™” CREATE INDEX idx_usage_stats_endpoint_date ON api_usage_stats(endpoint, DATE(created_at)); CREATE INDEX idx_usage_stats_status_date ON api_usage_stats(status_code, DATE(created_at)); ``` ## ๐Ÿ“ˆ ๋ฐ์ดํ„ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ „๋žต ### 1. ๊ธฐ์กด ๋ฐ์ดํ„ฐ ํ˜ธํ™˜์„ฑ ๋ณด์žฅ ```sql -- ๊ธฐ์กด ๋ฐ์ดํ„ฐ ๊ฒ€์ฆ ์ฟผ๋ฆฌ SELECT 'prds' as table_name, COUNT(*) as record_count, MIN(created_at) as oldest_record, MAX(updated_at) as latest_update FROM prds UNION ALL SELECT 'tasks', COUNT(*), MIN(created_at), MAX(updated_at) FROM tasks UNION ALL SELECT 'documents', COUNT(*), MIN(created_at), MAX(updated_at) FROM documents; ``` ### 2. API ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ```sql -- API ์Šคํ‚ค๋งˆ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ์‚ฝ์ž… INSERT INTO api_schemas (endpoint, method, category, description, input_schema, output_schema) VALUES -- PRD API ์Šคํ‚ค๋งˆ ('/api/prds', 'GET', 'prds', 'List all PRDs with optional filtering', '{"type": "object", "properties": {"status": {"type": "string"}, "project_id": {"type": "string"}}}', '{"type": "object", "properties": {"success": {"type": "boolean"}, "data": {"type": "array"}}}'), ('/api/prds', 'POST', 'prds', 'Create a new PRD', '{"type": "object", "required": ["title", "description"], "properties": {"title": {"type": "string"}, "description": {"type": "string"}}}', '{"type": "object", "properties": {"success": {"type": "boolean"}, "data": {"type": "object"}}}'), -- Task API ์Šคํ‚ค๋งˆ ('/api/tasks', 'GET', 'tasks', 'List all tasks with optional filtering', '{"type": "object", "properties": {"status": {"type": "string"}, "assignee": {"type": "string"}}}', '{"type": "object", "properties": {"success": {"type": "boolean"}, "data": {"type": "array"}}}'), ('/api/tasks', 'POST', 'tasks', 'Create a new task', '{"type": "object", "required": ["title", "description"], "properties": {"title": {"type": "string"}, "description": {"type": "string"}}}', '{"type": "object", "properties": {"success": {"type": "boolean"}, "data": {"type": "object"}}}'); ``` ### 3. Help ์ปจํ…์ธ  ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ```sql -- ๊ธฐ๋ณธ Help ์ปจํ…์ธ  ์‚ฝ์ž… INSERT INTO api_help_content (path, title, content, category, subcategory, order_index) VALUES ('/help/getting-started', 'WorkflowMCP API ์‹œ์ž‘ํ•˜๊ธฐ', '# API ์‹œ์ž‘ ๊ฐ€์ด๋“œ\n\n1. GET /api - ์ „์ฒด API ๊ตฌ์กฐ ํŒŒ์•…\n2. GET /api/help/getting-started - ์ด ๊ฐ€์ด๋“œ ์ฝ๊ธฐ\n3. ์›ํ•˜๋Š” ์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ\n4. ์˜ˆ์ œ ํ™•์ธ ํ›„ ์‚ฌ์šฉ', 'getting-started', 'overview', 1), ('/help/prds/overview', 'PRD ๊ด€๋ฆฌ API ๊ฐœ์š”', '# PRD Management API\n\nProduct Requirements Document๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์™„์ „ํ•œ API์ž…๋‹ˆ๋‹ค.\n\n## ๊ธฐ๋ณธ ์ž‘์—…\n- GET /api/prds - PRD ๋ชฉ๋ก\n- POST /api/prds - PRD ์ƒ์„ฑ\n- GET /api/prds/{id} - PRD ์กฐํšŒ', 'prds', 'overview', 1), ('/help/tasks/overview', 'Task ๊ด€๋ฆฌ API ๊ฐœ์š”', '# Task Management API\n\n์ž‘์—…์„ ์ƒ์„ฑ, ๊ด€๋ฆฌ, ์ถ”์ ํ•˜๋Š” API์ž…๋‹ˆ๋‹ค.\n\n## ๊ธฐ๋ณธ ์ž‘์—…\n- GET /api/tasks - ์ž‘์—… ๋ชฉ๋ก\n- POST /api/tasks - ์ž‘์—… ์ƒ์„ฑ\n- PUT /api/tasks/{id} - ์ž‘์—… ์ˆ˜์ •', 'tasks', 'overview', 1); ``` ## ๐Ÿ”„ ์ž๋™ ์œ ์ง€๋ณด์ˆ˜ ํ”„๋กœ์‹œ์ € ### 1. ์บ์‹œ ์ •๋ฆฌ ํ”„๋กœ์‹œ์ € ```sql -- ๋งŒ๋ฃŒ๋œ ์บ์‹œ ์ •๋ฆฌ (cron job์œผ๋กœ ์‹คํ–‰) CREATE TRIGGER cleanup_expired_cache AFTER INSERT ON api_cache BEGIN DELETE FROM api_cache WHERE expires_at < datetime('now', '-1 hour'); END; ``` ### 2. ์‚ฌ์šฉ ํ†ต๊ณ„ ์ง‘๊ณ„ ํ”„๋กœ์‹œ์ € ```sql -- ์ผ์ผ ์‚ฌ์šฉ ํ†ต๊ณ„ ์ง‘๊ณ„ (๋งค์ผ ์‹คํ–‰) CREATE VIEW daily_api_stats AS SELECT DATE(created_at) as date, endpoint, method, COUNT(*) as total_requests, COUNT(CASE WHEN status_code < 400 THEN 1 END) as successful_requests, COUNT(CASE WHEN status_code >= 400 THEN 1 END) as failed_requests, AVG(response_time_ms) as avg_response_time, MAX(response_time_ms) as max_response_time FROM api_usage_stats GROUP BY DATE(created_at), endpoint, method; ``` ### 3. ์‹ค์‹œ๊ฐ„ ์ด๋ฒคํŠธ ์ •๋ฆฌ ```sql -- ์ฒ˜๋ฆฌ๋œ ์ด๋ฒคํŠธ ์ •๋ฆฌ (1์ผ ํ›„) CREATE TRIGGER cleanup_processed_events AFTER UPDATE ON sse_events WHEN NEW.is_processed = TRUE BEGIN DELETE FROM sse_events WHERE is_processed = TRUE AND processed_at < datetime('now', '-1 day'); END; ``` ## ๐Ÿ“Š ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ชจ๋‹ˆํ„ฐ๋ง ### 1. ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ทฐ ```sql -- API ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง CREATE VIEW api_performance_monitor AS SELECT endpoint, method, COUNT(*) as requests_last_hour, AVG(response_time_ms) as avg_response_time, MAX(response_time_ms) as max_response_time, COUNT(CASE WHEN status_code >= 400 THEN 1 END) as error_count, ROUND(100.0 * COUNT(CASE WHEN status_code < 400 THEN 1 END) / COUNT(*), 2) as success_rate FROM api_usage_stats WHERE created_at > datetime('now', '-1 hour') GROUP BY endpoint, method; ``` ### 2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฑด๊ฐ•์„ฑ ์ฒดํฌ ```sql -- ํ…Œ์ด๋ธ” ํฌ๊ธฐ ๋ฐ ์„ฑ์žฅ๋ฅ  ๋ชจ๋‹ˆํ„ฐ๋ง CREATE VIEW db_health_check AS SELECT name as table_name, COUNT(*) as row_count, AVG(LENGTH(title || description || content)) as avg_content_size FROM sqlite_master m JOIN ( SELECT 'prds' as name, COUNT(*) as cnt FROM prds UNION ALL SELECT 'tasks' as name, COUNT(*) as cnt FROM tasks UNION ALL SELECT 'documents' as name, COUNT(*) as cnt FROM documents UNION ALL SELECT 'api_schemas' as name, COUNT(*) as cnt FROM api_schemas UNION ALL SELECT 'api_help_content' as name, COUNT(*) as cnt FROM api_help_content ) t ON m.name = t.name WHERE m.type = 'table'; ``` ## ๐ŸŽฏ ํ™•์žฅ์„ฑ ๊ณ ๋ ค์‚ฌํ•ญ ### 1. ํ–ฅํ›„ ํ…Œ์ด๋ธ” ํ™•์žฅ ์ค€๋น„ ```sql -- ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ์„ ์œ„ํ•œ ํ™•์žฅ ํ…Œ์ด๋ธ” (ํ–ฅํ›„) CREATE TABLE IF NOT EXISTS api_plugins ( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(100) NOT NULL UNIQUE, version VARCHAR(20) NOT NULL, category VARCHAR(100) NOT NULL, endpoints TEXT, -- JSON array of endpoints schema_definitions TEXT, -- JSON object of schemas help_content TEXT, -- JSON object of help content is_enabled BOOLEAN DEFAULT TRUE, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); ``` ### 2. ๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ ์ค€๋น„ (ํ–ฅํ›„) ```sql -- ๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ ์ง€์›์„ ์œ„ํ•œ ํ…Œ๋„ŒํŠธ ํ…Œ์ด๋ธ” (ํ–ฅํ›„) CREATE TABLE IF NOT EXISTS tenants ( id INTEGER PRIMARY KEY AUTOINCREMENT, tenant_id VARCHAR(100) NOT NULL UNIQUE, name VARCHAR(255) NOT NULL, api_quota INTEGER DEFAULT 10000, -- ์›”๊ฐ„ API ํ˜ธ์ถœ ํ•œ๋„ storage_quota INTEGER DEFAULT 1000, -- MB ๋‹จ์œ„ ์ €์žฅ์†Œ ํ•œ๋„ created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); ``` --- **์ž‘์„ฑ์ผ**: 2025-09-11 **์ž‘์„ฑ์ž**: ์‹œ์Šคํ…œ ์„ค๊ณ„์ž (Claude Code) **๊ฒ€ํ† ์ž**: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ด€๋ฆฌ์ž, ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž **๋ฒ„์ „**: 1.0 **์ƒํƒœ**: API ์ƒ์„ธ ์„ค๊ณ„ ๋Œ€๊ธฐ

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/foswmine/workflow-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server