From a3eb4a6b00d46d7d631412ae378ad1566781b486 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Thu, 19 Dec 2024 15:03:37 -0700 Subject: [PATCH 001/128] fix: reorganize services into their own folders Also renames some things for consistency. --- src/{durable-objects => auth}/auth-do.ts | 0 src/{durable-objects => cdn}/cdn-do.ts | 0 src/config.ts | 2 +- .../context-do.ts | 0 .../database-do.ts | 32 +++++------------ .../{aibtcdev.sql => database-schema.sql} | 0 src/durable-objects/README.md | 35 ------------------- src/durable-objects/index.ts | 8 ----- .../image-generator.do.ts} | 0 src/index.ts | 35 +++++++++++-------- .../metadata-generator.do.ts} | 2 +- .../scheduler-do.ts | 0 src/{durable-objects => tools}/tools-do.ts | 0 worker-configuration.d.ts | 4 +-- wrangler.toml | 21 ++++++----- 15 files changed, 46 insertions(+), 93 deletions(-) rename src/{durable-objects => auth}/auth-do.ts (100%) rename src/{durable-objects => cdn}/cdn-do.ts (100%) rename src/{durable-objects => context}/context-do.ts (100%) rename src/{durable-objects => database}/database-do.ts (97%) rename src/database/{aibtcdev.sql => database-schema.sql} (100%) delete mode 100644 src/durable-objects/README.md delete mode 100644 src/durable-objects/index.ts rename src/{durable-objects/image-do.ts => image-generator/image-generator.do.ts} (100%) rename src/{durable-objects/metadata-do.ts => metadata-generator/metadata-generator.do.ts} (99%) rename src/{durable-objects => scheduler}/scheduler-do.ts (100%) rename src/{durable-objects => tools}/tools-do.ts (100%) diff --git a/src/durable-objects/auth-do.ts b/src/auth/auth-do.ts similarity index 100% rename from src/durable-objects/auth-do.ts rename to src/auth/auth-do.ts diff --git a/src/durable-objects/cdn-do.ts b/src/cdn/cdn-do.ts similarity index 100% rename from src/durable-objects/cdn-do.ts rename to src/cdn/cdn-do.ts diff --git a/src/config.ts b/src/config.ts index ff93ec3..74e86cf 100644 --- a/src/config.ts +++ b/src/config.ts @@ -34,7 +34,7 @@ export class AppConfig { return { // List of all supported API service endpoints // Used for routing and validation - SUPPORTED_SERVICES: ['/auth', '/cdn', '/context', '/database', '/scheduler', '/tools', '/image', '/metadata'], + SUPPORTED_SERVICES: ['/auth', '/cdn', '/context', '/database', '/image-generator', '/metadata-generator', '/scheduler', '/tools'], // Default interval for Durable Object alarm checks // Currently set to 8 hours while alarm functionality is in development diff --git a/src/durable-objects/context-do.ts b/src/context/context-do.ts similarity index 100% rename from src/durable-objects/context-do.ts rename to src/context/context-do.ts diff --git a/src/durable-objects/database-do.ts b/src/database/database-do.ts similarity index 97% rename from src/durable-objects/database-do.ts rename to src/database/database-do.ts index 3db71c5..a38ab13 100644 --- a/src/durable-objects/database-do.ts +++ b/src/database/database-do.ts @@ -15,18 +15,9 @@ import { XBotAuthorsTable, XBotLogsTable, XBotTweetsTable, -} from '../database/models'; -import { getAgents, createAgent, updateAgent, deleteAgent } from '../database/helpers/agents'; -import { - getAuthor, - addAuthor, - getTweet, - getThreadTweets, - getAuthorTweets, - addTweet, - getTweetLogs, - addLog, -} from '../database/helpers/twitter'; +} from './models'; +import { getAgents, createAgent, updateAgent, deleteAgent } from './helpers/agents'; +import { getAuthor, addAuthor, getTweet, getThreadTweets, getAuthorTweets, addTweet, getTweetLogs, addLog } from './helpers/twitter'; import { createCrew, getCrew, @@ -39,15 +30,8 @@ import { getExecutionSteps, createExecutionStep, deleteExecutionSteps, -} from '../database/helpers/crews'; -import { - getCronsByCrew, - createCron, - updateCronInput, - toggleCronStatus, - getEnabledCrons, - getEnabledCronsDetailed, -} from '../database/helpers/crons'; +} from './helpers/crews'; +import { getCronsByCrew, createCron, updateCronInput, toggleCronStatus, getEnabledCrons, getEnabledCronsDetailed } from './helpers/crons'; import { getUserRole, getUserProfile, @@ -56,9 +40,9 @@ import { deleteUserProfile, getAllUserProfiles, updateUserProfileById, -} from '../database/helpers/profiles'; -import { addConversation, getConversationHistory, getConversations, getLatestConversation } from '../database/helpers/conversations'; -import { getTask, getTasks, createTask, updateTask, deleteTask, deleteTasks } from '../database/helpers/tasks'; +} from './helpers/profiles'; +import { addConversation, getConversationHistory, getConversations, getLatestConversation } from './helpers/conversations'; +import { getTask, getTasks, createTask, updateTask, deleteTask, deleteTasks } from './helpers/tasks'; import { validateSessionToken, validateSharedKeyAuth } from '../utils/auth-helper'; /** diff --git a/src/database/aibtcdev.sql b/src/database/database-schema.sql similarity index 100% rename from src/database/aibtcdev.sql rename to src/database/database-schema.sql diff --git a/src/durable-objects/README.md b/src/durable-objects/README.md deleted file mode 100644 index a947a91..0000000 --- a/src/durable-objects/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Durable Objects - -This directory contains Cloudflare Durable Object implementations that provide stateful services for the application. - -## Common Features - -All Durable Objects share these common patterns: - -- Constructor initializes with DurableObjectState and Env -- Alarm handling for periodic tasks -- Base path routing with supported endpoints -- Standard error handling and response formatting - -## Objects Overview - -- **AuthDO**: Handles authentication flows and session management -- **CdnDO**: Manages R2 bucket operations for content delivery -- **DatabaseDO**: Manages database operations and queries -- 🚧 **ContextDO**: Provides transformed data as API endpoints -- 🚧 **SchedulerDO**: Handles scheduling and execution of backend jobs -- 🚧 **ToolsDO**: Runs TypeScript tools requested by the backend - -## Usage - -Each Durable Object is bound to a specific namespace in the worker configuration and can be accessed through that binding. - -Example: - -```typescript -// Get Auth DO stub -const id = env.AUTH_DO.idFromName('auth'); -const stub = env.AUTH_DO.get(id); -// Make request to DO -const response = await stub.fetch(request); -``` diff --git a/src/durable-objects/index.ts b/src/durable-objects/index.ts deleted file mode 100644 index 573b3d2..0000000 --- a/src/durable-objects/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * from './auth-do'; -export * from './cdn-do'; -export * from './context-do'; -export * from './database-do'; -export * from './scheduler-do'; -export * from './tools-do'; -export * from './image-do'; -export * from './metadata-do'; diff --git a/src/durable-objects/image-do.ts b/src/image-generator/image-generator.do.ts similarity index 100% rename from src/durable-objects/image-do.ts rename to src/image-generator/image-generator.do.ts diff --git a/src/index.ts b/src/index.ts index f41e3a6..6f587f9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,17 @@ import { Env } from '../worker-configuration'; import { AppConfig } from './config'; import { corsHeaders, createJsonResponse } from './utils/requests-responses'; -import { AuthDO, CdnDO, ContextDO, DatabaseDO, SchedulerDO, ToolsDO, ImageGeneratorDO, TokenMetadataDO } from './durable-objects'; +import { AuthDO } from './auth/auth-do'; +import { CdnDO } from './cdn/cdn-do'; +import { ContextDO } from './context/context-do'; +import { DatabaseDO } from './database/database-do'; +import { ImageGeneratorDO } from './image-generator/image-generator.do'; +import { MetadataGeneratorDO } from './metadata-generator/metadata-generator.do'; +import { SchedulerDO } from './scheduler/scheduler-do'; +import { ToolsDO } from './tools/tools-do'; // export the Durable Object classes we're using -export { AuthDO, ContextDO, DatabaseDO, SchedulerDO, ToolsDO, CdnDO, ImageGeneratorDO, TokenMetadataDO }; +export { AuthDO, CdnDO, ContextDO, DatabaseDO, ImageGeneratorDO, MetadataGeneratorDO, SchedulerDO, ToolsDO }; export default { /** @@ -60,27 +67,27 @@ export default { return await stub.fetch(request); // forward the request to the Durable Object } - if (path.startsWith('/scheduler')) { - let id: DurableObjectId = env.SCHEDULER_DO.idFromName('scheduler-do'); // create the instance - let stub = env.SCHEDULER_DO.get(id); // get the stub for communication + if (path.startsWith('/image-generator')) { + let id: DurableObjectId = env.IMAGES_DO.idFromName('image-generator-do'); // create the instance + let stub = env.IMAGES_DO.get(id); // get the stub for communication return await stub.fetch(request); // forward the request to the Durable Object } - if (path.startsWith('/tools')) { - let id: DurableObjectId = env.TOOLS_DO.idFromName('tools-do'); // create the instance - let stub = env.TOOLS_DO.get(id); // get the stub for communication + if (path.startsWith('/metadata-generator')) { + let id: DurableObjectId = env.METADATA_DO.idFromName('metadata-generator-do'); // create the instance + let stub = env.METADATA_GENERATOR_DO.get(id); // get the stub for communication return await stub.fetch(request); // forward the request to the Durable Object } - if (path.startsWith('/image')) { - let id: DurableObjectId = env.IMAGES_DO.idFromName('image-do'); // create the instance - let stub = env.IMAGES_DO.get(id); // get the stub for communication + if (path.startsWith('/scheduler')) { + let id: DurableObjectId = env.SCHEDULER_DO.idFromName('scheduler-do'); // create the instance + let stub = env.SCHEDULER_DO.get(id); // get the stub for communication return await stub.fetch(request); // forward the request to the Durable Object } - if (path.startsWith('/metadata')) { - let id: DurableObjectId = env.METADATA_DO.idFromName('metadata-do'); // create the instance - let stub = env.METADATA_DO.get(id); // get the stub for communication + if (path.startsWith('/tools')) { + let id: DurableObjectId = env.TOOLS_DO.idFromName('tools-do'); // create the instance + let stub = env.TOOLS_DO.get(id); // get the stub for communication return await stub.fetch(request); // forward the request to the Durable Object } diff --git a/src/durable-objects/metadata-do.ts b/src/metadata-generator/metadata-generator.do.ts similarity index 99% rename from src/durable-objects/metadata-do.ts rename to src/metadata-generator/metadata-generator.do.ts index 2da2f3c..3e24d18 100644 --- a/src/durable-objects/metadata-do.ts +++ b/src/metadata-generator/metadata-generator.do.ts @@ -28,7 +28,7 @@ interface GenerateMetadataRequest { imagePrompt?: string; } -export class TokenMetadataDO extends DurableObject { +export class MetadataGeneratorDO extends DurableObject { private readonly BASE_PATH = '/metadata'; private readonly KEY_PREFIX = 'sip10'; private readonly IMAGE_DO_NAME = 'IMAGE_GENERATOR'; diff --git a/src/durable-objects/scheduler-do.ts b/src/scheduler/scheduler-do.ts similarity index 100% rename from src/durable-objects/scheduler-do.ts rename to src/scheduler/scheduler-do.ts diff --git a/src/durable-objects/tools-do.ts b/src/tools/tools-do.ts similarity index 100% rename from src/durable-objects/tools-do.ts rename to src/tools/tools-do.ts diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index 003e23b..ba4de55 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -10,8 +10,8 @@ export interface Env { DATABASE_DO: DurableObjectNamespace; SCHEDULER_DO: DurableObjectNamespace; TOOLS_DO: DurableObjectNamespace; - IMAGES_DO: DurableObjectNamespace; - METADATA_DO: DurableObjectNamespace; + IMAGE_GENERATOR_DO: DurableObjectNamespace; + METADATA_GENERATOR_DO: DurableObjectNamespace; CDN_DO: DurableObjectNamespace; AIBTCDEV_SERVICES_BUCKET: R2Bucket; AIBTCDEV_SERVICES_DB: D1Database; diff --git a/wrangler.toml b/wrangler.toml index 8400fcd..22fe7f7 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -38,20 +38,20 @@ name = "DATABASE_DO" class_name = "DatabaseDO" [[durable_objects.bindings]] -name = "SCHEDULER_DO" -class_name = "SchedulerDO" +name = "IMAGE_GENERATOR_DO" +class_name = "ImageGeneratorDO" [[durable_objects.bindings]] -name = "TOOLS_DO" -class_name = "ToolsDO" +name = "METADATA_GENERATOR_DO" +class_name = "MetadataGeneratorDO" [[durable_objects.bindings]] -name = "IMAGES_DO" -class_name = "ImageGeneratorDO" +name = "SCHEDULER_DO" +class_name = "SchedulerDO" [[durable_objects.bindings]] -name = "METADATA_DO" -class_name = "TokenMetadataDO" +name = "TOOLS_DO" +class_name = "ToolsDO" # Durable Object migrations. # Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#migrations @@ -67,6 +67,11 @@ new_classes = ["CdnDO"] tag = "v3" new_classes = ["ImageGeneratorDO", "TokenMetadataDO"] +[[migrations]] +tag = "v4" +new_classes = ["MetadataGeneratorDO"] +deleted_classes = ["TokenMetadataDO"] + # Bind a KV Namespace. Use KV as persistent storage for small key-value pairs. # Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#kv-namespaces [[kv_namespaces]] From f21ce9f29deacd05df9c78ca019918f8b98b3306 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Thu, 19 Dec 2024 15:04:34 -0700 Subject: [PATCH 002/128] fix: one more spot to update names --- src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 6f587f9..13fc031 100644 --- a/src/index.ts +++ b/src/index.ts @@ -68,13 +68,13 @@ export default { } if (path.startsWith('/image-generator')) { - let id: DurableObjectId = env.IMAGES_DO.idFromName('image-generator-do'); // create the instance - let stub = env.IMAGES_DO.get(id); // get the stub for communication + let id: DurableObjectId = env.IMAGE_GENERATOR_DO.idFromName('image-generator-do'); // create the instance + let stub = env.IMAGE_GENERATOR_DO.get(id); // get the stub for communication return await stub.fetch(request); // forward the request to the Durable Object } if (path.startsWith('/metadata-generator')) { - let id: DurableObjectId = env.METADATA_DO.idFromName('metadata-generator-do'); // create the instance + let id: DurableObjectId = env.METADATA_GENERATOR_DO.idFromName('metadata-generator-do'); // create the instance let stub = env.METADATA_GENERATOR_DO.get(id); // get the stub for communication return await stub.fetch(request); // forward the request to the Durable Object } From 9fe8168bd36ae48e176c88dea4d30ea841dd06b3 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Thu, 19 Dec 2024 21:46:30 -0700 Subject: [PATCH 003/128] docs: add context for aider --- src/consistent_endpoints.md | 75 +++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/consistent_endpoints.md diff --git a/src/consistent_endpoints.md b/src/consistent_endpoints.md new file mode 100644 index 0000000..e46872e --- /dev/null +++ b/src/consistent_endpoints.md @@ -0,0 +1,75 @@ +# Implementation Guide: createJsonResponse + +This guide outlines the standardized implementation of `createJsonResponse` across all endpoints. + +## Response Formats + +### 1. Successful Responses (Status Codes 200-299) + +#### When Returning Data + +```javascript +createJsonResponse({ + message: 'A clear description of what succeeded', + data: theActualData, +}); +``` + +#### When Returning Message Only + +```javascript +createJsonResponse({ + message: 'A clear description of what succeeded', +}); +``` + +### 2. Error Responses (Status Codes 300+) + +Use a direct string message: + +```javascript +createJsonResponse('A clear description of what went wrong', errorStatusCode); +``` + +## Implementation Guidelines + +### 3. Specific Patterns + +- Never wrap response data in unnecessary object literals (e.g., avoid `{ result }` when `result` is already an object) +- Always include a descriptive message for successful operations +- Keep error messages concise and descriptive +- Use consistent status codes for similar types of errors + +### 4. Implementation Examples + +#### Success with Data + +```javascript +return createJsonResponse({ + message: 'Successfully retrieved crews', + data: crews, +}); +``` + +#### Success with Message Only + +```javascript +return createJsonResponse({ + message: `Successfully deleted ${key}`, +}); +``` + +#### Error Response + +```javascript +return createJsonResponse('Missing required parameter: address', 400); +``` + +## Response Structure + +The `createJsonResponse` function automatically wraps these responses in a standardized format that includes: + +- A success boolean flag +- Either a data or error property based on the status code + +This standardization ensures consistent response structures across all endpoints and makes it easier for clients to handle responses predictably. From b2f29421d15e8b1cea9ff153eb8e3318506c24b6 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 21:52:19 -0700 Subject: [PATCH 004/128] refactor: Update response formats for conversations endpoints --- src/database/database-do.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index a38ab13..a7d7882 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -151,7 +151,10 @@ export class DatabaseDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { return createJsonResponse({ - message: `Supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, + message: 'Database service endpoints available', + data: { + endpoints: this.SUPPORTED_ENDPOINTS + } }); } @@ -167,28 +170,37 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/conversations') { const address = url.searchParams.get('address'); if (!address) { - return createJsonResponse({ error: 'Missing address parameter' }, 400); + return createJsonResponse('Missing address parameter', 400); } const conversations = await getConversations(this.orm, address); - return createJsonResponse({ conversations }); + return createJsonResponse({ + message: 'Successfully retrieved conversations', + data: conversations + }); } if (endpoint === '/conversations/latest') { const address = url.searchParams.get('address'); if (!address) { - return createJsonResponse({ error: 'Missing address parameter' }, 400); + return createJsonResponse('Missing address parameter', 400); } const conversation = await getLatestConversation(this.orm, address); - return createJsonResponse({ conversation }); + return createJsonResponse({ + message: 'Successfully retrieved latest conversation', + data: conversation + }); } if (endpoint === '/conversations/history') { const conversationId = url.searchParams.get('id'); if (!conversationId) { - return createJsonResponse({ error: 'Missing id parameter' }, 400); + return createJsonResponse('Missing conversation ID parameter', 400); } const history = await getConversationHistory(this.orm, parseInt(conversationId)); - return createJsonResponse({ history }); + return createJsonResponse({ + message: 'Successfully retrieved conversation history', + data: history + }); } // Crew endpoints From f665597f7ef0640d21ddfbe693a7cfa87c8c7af0 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 21:54:32 -0700 Subject: [PATCH 005/128] feat: Standardize response formats and error handling in database-do.ts --- src/database/database-do.ts | 52 +++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index a7d7882..4fa115a 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -162,7 +162,7 @@ export class DatabaseDO extends DurableObject { // frontend and backend each have their own stored in KV const authResult = await validateSharedKeyAuth(this.env, request); if (!authResult.success) { - return createJsonResponse({ error: authResult.error }, authResult.status); + return createJsonResponse(authResult.error, authResult.status); } try { @@ -207,13 +207,13 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/crews/profile') { const address = url.searchParams.get('address'); if (!address) { - return createJsonResponse({ error: 'Missing address parameter' }, 400); + return createJsonResponse('Missing address parameter', 400); } // Get the session token from Authorization header const authHeader = request.headers.get('Authorization'); if (!authHeader) { - return createJsonResponse({ error: 'Missing authorization header' }, 401); + return createJsonResponse('Missing authorization header', 401); } // Extract token from Bearer format @@ -222,62 +222,80 @@ export class DatabaseDO extends DurableObject { // Verify the token matches the requested address const tokenAddress = await validateSessionToken(this.env, token); if (!tokenAddress.success || tokenAddress.address !== address) { - return createJsonResponse({ error: 'Unauthorized access' }, 403); + return createJsonResponse('Unauthorized access', 403); } const crews = await getCrewsByProfile(this.orm, address); - return createJsonResponse({ crews }); + return createJsonResponse({ + message: 'Successfully retrieved profile crews', + data: crews + }); } if (endpoint === '/crews/public') { const crews = await getPublicCrews(this.orm); - return createJsonResponse({ crews }); + return createJsonResponse({ + message: 'Successfully retrieved public crews', + data: crews + }); } if (endpoint === '/crews/get') { const crewId = url.searchParams.get('id'); if (!crewId) { - return createJsonResponse({ error: 'Missing id parameter' }, 400); + return createJsonResponse('Missing id parameter', 400); } const crew = await getCrew(this.orm, parseInt(crewId)); - return createJsonResponse({ crew }); + return createJsonResponse({ + message: 'Successfully retrieved crew', + data: crew + }); } if (endpoint === '/crews/create') { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createJsonResponse('Method not allowed', 405); } const crewData = (await request.json()) as Omit; if (!crewData.profile_id || !crewData.crew_name) { - return createJsonResponse({ error: 'Missing required fields: profile_id, crew_name' }, 400); + return createJsonResponse('Missing required fields: profile_id, crew_name', 400); } const crew = await createCrew(this.orm, crewData); - return createJsonResponse({ crew }); + return createJsonResponse({ + message: 'Successfully created crew', + data: crew + }); } if (endpoint === '/crews/update') { if (request.method !== 'PUT') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createJsonResponse('Method not allowed', 405); } const crewId = url.searchParams.get('id'); if (!crewId) { - return createJsonResponse({ error: 'Missing id parameter' }, 400); + return createJsonResponse('Missing id parameter', 400); } const updates = (await request.json()) as Partial>; const result = await updateCrew(this.orm, parseInt(crewId), updates); - return createJsonResponse({ result }); + return createJsonResponse({ + message: 'Successfully updated crew', + data: result + }); } if (endpoint === '/crews/delete') { if (request.method !== 'DELETE') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createJsonResponse('Method not allowed', 405); } const crewId = url.searchParams.get('id'); if (!crewId) { - return createJsonResponse({ error: 'Missing id parameter' }, 400); + return createJsonResponse('Missing id parameter', 400); } const result = await deleteCrew(this.orm, parseInt(crewId)); - return createJsonResponse({ result }); + return createJsonResponse({ + message: 'Successfully deleted crew', + data: result + }); } // Agent endpoints From 51cbf7b8593a36fe51632c87ffdce7b9c5c50abc Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:05:32 -0700 Subject: [PATCH 006/128] refactor: Standardize Agent endpoint response formats and error handling --- src/database/database-do.ts | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 4fa115a..69ef994 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -302,15 +302,18 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/agents/get') { const crewId = url.searchParams.get('crewId'); if (!crewId) { - return createJsonResponse({ error: 'Missing crewId parameter' }, 400); + return createJsonResponse('Missing crewId parameter', 400); } const agents = await getAgents(this.orm, parseInt(crewId)); - return createJsonResponse({ agents }); + return createJsonResponse({ + message: 'Successfully retrieved agents', + data: agents + }); } if (endpoint === '/agents/create') { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createJsonResponse('Method not allowed', 405); } const agentData = (await request.json()) as Omit; if ( @@ -321,37 +324,46 @@ export class DatabaseDO extends DurableObject { !agentData.agent_goal || !agentData.agent_backstory ) { - return createJsonResponse({ error: 'Missing required fields' }, 400); + return createJsonResponse('Missing required fields: profile_id, crew_id, agent_name, agent_role, agent_goal, agent_backstory', 400); } const agent = await createAgent(this.orm, agentData); - return createJsonResponse({ agent }); + return createJsonResponse({ + message: 'Successfully created agent', + data: agent + }); } if (endpoint === '/agents/update') { if (request.method !== 'PUT') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createJsonResponse('Method not allowed', 405); } const agentId = url.searchParams.get('id'); if (!agentId) { - return createJsonResponse({ error: 'Missing id parameter' }, 400); + return createJsonResponse('Missing id parameter', 400); } const updates = (await request.json()) as Partial< Omit >; const result = await updateAgent(this.orm, parseInt(agentId), updates); - return createJsonResponse({ result }); + return createJsonResponse({ + message: 'Successfully updated agent', + data: result + }); } if (endpoint === '/agents/delete') { if (request.method !== 'DELETE') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createJsonResponse('Method not allowed', 405); } const agentId = url.searchParams.get('id'); if (!agentId) { - return createJsonResponse({ error: 'Missing id parameter' }, 400); + return createJsonResponse('Missing id parameter', 400); } const result = await deleteAgent(this.orm, parseInt(agentId)); - return createJsonResponse({ result }); + return createJsonResponse({ + message: 'Successfully deleted agent', + data: result + }); } // Task endpoints From 26ec3713de1198f1c01f9877ae6a84eef7af299d Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:06:05 -0700 Subject: [PATCH 007/128] refactor: Standardize Task endpoint responses with consistent error and success messages --- src/database/database-do.ts | 50 +++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 69ef994..1354ecf 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -370,24 +370,30 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/tasks/get') { const taskId = url.searchParams.get('id'); if (!taskId) { - return createJsonResponse({ error: 'Missing id parameter' }, 400); + return createJsonResponse('Missing id parameter', 400); } const task = await getTask(this.orm, parseInt(taskId)); - return createJsonResponse({ task }); + return createJsonResponse({ + message: 'Successfully retrieved task', + data: task + }); } if (endpoint === '/tasks/list') { const agentId = url.searchParams.get('agentId'); if (!agentId) { - return createJsonResponse({ error: 'Missing agentId parameter' }, 400); + return createJsonResponse('Missing agentId parameter', 400); } const tasks = await getTasks(this.orm, parseInt(agentId)); - return createJsonResponse({ tasks }); + return createJsonResponse({ + message: 'Successfully retrieved tasks', + data: tasks + }); } if (endpoint === '/tasks/create') { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createJsonResponse('Method not allowed', 405); } const taskData = (await request.json()) as UserTasksTable; if ( @@ -398,49 +404,61 @@ export class DatabaseDO extends DurableObject { !taskData.task_description || !taskData.task_expected_output ) { - return createJsonResponse({ error: 'Missing required fields' }, 400); + return createJsonResponse('Missing required fields: profile_id, crew_id, agent_id, task_name, task_description, task_expected_output', 400); } const task = await createTask(this.orm, taskData); - return createJsonResponse({ task }); + return createJsonResponse({ + message: 'Successfully created task', + data: task + }); } if (endpoint === '/tasks/update') { if (request.method !== 'PUT') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createJsonResponse('Method not allowed', 405); } const taskId = url.searchParams.get('id'); if (!taskId) { - return createJsonResponse({ error: 'Missing id parameter' }, 400); + return createJsonResponse('Missing id parameter', 400); } const updates = (await request.json()) as Partial< Omit >; const result = await updateTask(this.orm, parseInt(taskId), updates); - return createJsonResponse({ result }); + return createJsonResponse({ + message: 'Successfully updated task', + data: result + }); } if (endpoint === '/tasks/delete') { if (request.method !== 'DELETE') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createJsonResponse('Method not allowed', 405); } const taskId = url.searchParams.get('id'); if (!taskId) { - return createJsonResponse({ error: 'Missing id parameter' }, 400); + return createJsonResponse('Missing id parameter', 400); } const result = await deleteTask(this.orm, parseInt(taskId)); - return createJsonResponse({ result }); + return createJsonResponse({ + message: 'Successfully deleted task', + data: result + }); } if (endpoint === '/tasks/delete-all') { if (request.method !== 'DELETE') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createJsonResponse('Method not allowed', 405); } const agentId = url.searchParams.get('agentId'); if (!agentId) { - return createJsonResponse({ error: 'Missing agentId parameter' }, 400); + return createJsonResponse('Missing agentId parameter', 400); } const result = await deleteTasks(this.orm, parseInt(agentId)); - return createJsonResponse({ result }); + return createJsonResponse({ + message: 'Successfully deleted all tasks for agent', + data: result + }); } if (endpoint === '/crews/executions') { From 1dc2b71310e9d750cbbed267a46c274023410840 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:08:02 -0700 Subject: [PATCH 008/128] refactor: Standardize API response structure for multiple endpoints --- src/database/database-do.ts | 42 ++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 1354ecf..89c4e2f 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -464,10 +464,13 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/crews/executions') { const address = url.searchParams.get('address'); if (!address) { - return createJsonResponse({ error: 'Missing address parameter' }, 400); + return createJsonResponse('Missing address parameter', 400); } const executions = await getCrewExecutions(this.orm, address); - return createJsonResponse({ executions }); + return createJsonResponse({ + message: 'Successfully retrieved crew executions', + data: executions + }); } if (endpoint === '/crews/executions/add') { @@ -664,10 +667,13 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/twitter/authors/get') { const authorId = url.searchParams.get('authorId'); if (!authorId) { - return createJsonResponse({ error: 'Missing authorId parameter' }, 400); + return createJsonResponse('Missing authorId parameter', 400); } const author = await getAuthor(this.orm, authorId); - return createJsonResponse({ author }); + return createJsonResponse({ + message: 'Successfully retrieved author', + data: author + }); } if (endpoint === '/twitter/authors/create') { @@ -685,28 +691,37 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/twitter/tweets/get') { const tweetId = url.searchParams.get('tweetId'); if (!tweetId) { - return createJsonResponse({ error: 'Missing tweetId parameter' }, 400); + return createJsonResponse('Missing tweetId parameter', 400); } const tweet = await getTweet(this.orm, tweetId); - return createJsonResponse({ tweet }); + return createJsonResponse({ + message: 'Successfully retrieved tweet', + data: tweet + }); } if (endpoint === '/twitter/tweets/thread') { const threadId = url.searchParams.get('threadId'); if (!threadId) { - return createJsonResponse({ error: 'Missing threadId parameter' }, 400); + return createJsonResponse('Missing threadId parameter', 400); } const tweets = await getThreadTweets(this.orm, parseInt(threadId)); - return createJsonResponse({ tweets }); + return createJsonResponse({ + message: 'Successfully retrieved thread tweets', + data: tweets + }); } if (endpoint === '/twitter/tweets/author') { const authorId = url.searchParams.get('authorId'); if (!authorId) { - return createJsonResponse({ error: 'Missing authorId parameter' }, 400); + return createJsonResponse('Missing authorId parameter', 400); } const tweets = await getAuthorTweets(this.orm, authorId); - return createJsonResponse({ tweets }); + return createJsonResponse({ + message: 'Successfully retrieved author tweets', + data: tweets + }); } if (endpoint === '/twitter/tweets/add') { @@ -732,10 +747,13 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/twitter/logs/get') { const tweetId = url.searchParams.get('tweetId'); if (!tweetId) { - return createJsonResponse({ error: 'Missing tweetId parameter' }, 400); + return createJsonResponse('Missing tweetId parameter', 400); } const logs = await getTweetLogs(this.orm, tweetId); - return createJsonResponse({ logs }); + return createJsonResponse({ + message: 'Successfully retrieved tweet logs', + data: logs + }); } if (endpoint === '/twitter/logs/add') { From a02924399fb383c876c1b52362b8314efd25829f Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:10:53 -0700 Subject: [PATCH 009/128] refactor: Standardize error and success responses in CDN and Auth DOs --- src/auth/auth-do.ts | 14 ++------------ src/cdn/cdn-do.ts | 39 ++++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/auth/auth-do.ts b/src/auth/auth-do.ts index fcf6612..23adcac 100644 --- a/src/auth/auth-do.ts +++ b/src/auth/auth-do.ts @@ -87,12 +87,7 @@ export class AuthDO extends DurableObject { // get signature from body const body = await request.json(); if (!body || typeof body !== 'object' || !('signature' in body) || !('publicKey' in body)) { - return createJsonResponse( - { - error: 'Missing or invalid "signature" or "publicKey" in request body', - }, - 400 - ); + return createJsonResponse('Missing or invalid "signature" or "publicKey" in request body', 400); } const signedMessage = String(body.signature); const publicKey = String(body.publicKey); @@ -108,12 +103,7 @@ export class AuthDO extends DurableObject { const isAddressValid = validateStacksAddress(addressFromPubkey); // check if signature is valid with the public key if (!isSignatureVerified) { - return createJsonResponse( - { - error: `Signature verification failed for public key ${publicKey}`, - }, - 401 - ); + return createJsonResponse(`Signature verification failed for public key ${publicKey}`, 401); } // check if address is valid if (!isAddressValid) { diff --git a/src/cdn/cdn-do.ts b/src/cdn/cdn-do.ts index 8b2e6af..b16a5f5 100644 --- a/src/cdn/cdn-do.ts +++ b/src/cdn/cdn-do.ts @@ -68,7 +68,7 @@ export class CdnDO extends DurableObject { const object = await this.env.AIBTCDEV_SERVICES_BUCKET.get(r2ObjectKey); if (!object) { - return createJsonResponse({ error: 'Object not found' }, 404); + return createJsonResponse('Object not found', 404); } // Return the object with appropriate headers @@ -80,7 +80,7 @@ export class CdnDO extends DurableObject { }, }); } catch (error) { - return createJsonResponse({ error: 'Failed to retrieve object' }, 500); + return createJsonResponse('Failed to retrieve object', 500); } } @@ -101,15 +101,18 @@ export class CdnDO extends DurableObject { const objects = await this.env.AIBTCDEV_SERVICES_BUCKET.list(options); return createJsonResponse({ - objects: objects.objects.map((obj) => ({ - key: obj.key, - size: obj.size, - uploaded: obj.uploaded, - etag: obj.etag, - httpEtag: obj.httpEtag, - })), - truncated: objects.truncated, - cursor: objects.truncated ? objects.cursor : undefined, + message: 'Successfully listed objects', + data: { + objects: objects.objects.map((obj) => ({ + key: obj.key, + size: obj.size, + uploaded: obj.uploaded, + etag: obj.etag, + httpEtag: obj.httpEtag, + })), + truncated: objects.truncated, + cursor: objects.truncated ? objects.cursor : undefined, + } }); } catch (error) { return createJsonResponse({ error: 'Failed to list objects' }, 500); @@ -134,18 +137,24 @@ export class CdnDO extends DurableObject { }, }); - return createJsonResponse({ success: true, r2ObjectKey, etag: object.httpEtag }); + return createJsonResponse({ + message: 'Successfully stored object', + data: { r2ObjectKey, etag: object.httpEtag } + }); } catch (error) { - return createJsonResponse({ error: 'Failed to store object' }, 500); + return createJsonResponse('Failed to store object', 500); } } if (endpoint === '/delete') { try { await this.env.AIBTCDEV_SERVICES_BUCKET.delete(r2ObjectKey); - return createJsonResponse({ success: true, r2ObjectKey }); + return createJsonResponse({ + message: 'Successfully deleted object', + data: { r2ObjectKey } + }); } catch (error) { - return createJsonResponse({ error: 'Failed to delete object' }, 500); + return createJsonResponse('Failed to delete object', 500); } } From 9cf481e901c7d86dadccf031c1c1d71003f376ab Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:11:23 -0700 Subject: [PATCH 010/128] refactor: Standardize error response formats across services --- src/auth/auth-do.ts | 21 +++------------------ src/cdn/cdn-do.ts | 14 ++------------ src/index.ts | 7 +------ 3 files changed, 6 insertions(+), 36 deletions(-) diff --git a/src/auth/auth-do.ts b/src/auth/auth-do.ts index 23adcac..a5be738 100644 --- a/src/auth/auth-do.ts +++ b/src/auth/auth-do.ts @@ -48,12 +48,7 @@ export class AuthDO extends DurableObject { const path = url.pathname; if (!path.startsWith(this.BASE_PATH)) { - return createJsonResponse( - { - error: `Request at ${path} does not start with base path ${this.BASE_PATH}`, - }, - 404 - ); + return createJsonResponse(`Request at ${path} does not start with base path ${this.BASE_PATH}`, 404); } // Remove base path to get the endpoint @@ -75,12 +70,7 @@ export class AuthDO extends DurableObject { // all methods from this point forward are POST if (request.method !== 'POST') { - return createJsonResponse( - { - error: `Unsupported method: ${request.method}, supported method: POST`, - }, - 405 - ); + return createJsonResponse(`Unsupported method: ${request.method}, supported method: POST`, 405); } if (endpoint === '/request-auth-token') { @@ -219,11 +209,6 @@ export class AuthDO extends DurableObject { // TODO: endpoint to revoke a session token - return createJsonResponse( - { - error: `Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, - }, - 404 - ); + return createJsonResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); } } diff --git a/src/cdn/cdn-do.ts b/src/cdn/cdn-do.ts index b16a5f5..8c7ab97 100644 --- a/src/cdn/cdn-do.ts +++ b/src/cdn/cdn-do.ts @@ -42,12 +42,7 @@ export class CdnDO extends DurableObject { const path = url.pathname; if (!path.startsWith(this.BASE_PATH)) { - return createJsonResponse( - { - error: `Request at ${path} does not start with base path ${this.BASE_PATH}`, - }, - 404 - ); + return createJsonResponse(`Request at ${path} does not start with base path ${this.BASE_PATH}`, 404); } // Remove base path to get the endpoint @@ -158,11 +153,6 @@ export class CdnDO extends DurableObject { } } - return createJsonResponse( - { - error: `Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, - }, - 404 - ); + return createJsonResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); } } diff --git a/src/index.ts b/src/index.ts index 13fc031..9165b01 100644 --- a/src/index.ts +++ b/src/index.ts @@ -92,11 +92,6 @@ export default { } // Return 404 for any other path - return createJsonResponse( - { - error: `Unsupported service at: ${path}, supported services: ${config.SUPPORTED_SERVICES.join(', ')}`, - }, - 404 - ); + return createJsonResponse(`Unsupported service at: ${path}, supported services: ${config.SUPPORTED_SERVICES.join(', ')}`, 404); }, } satisfies ExportedHandler; From c49acd5d9b0fe64d43a8526fcdbbd5680f616a8d Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:12:58 -0700 Subject: [PATCH 011/128] refactor: Standardize error responses across auth and cdn endpoints --- src/auth/auth-do.ts | 30 +++++------------------------- src/cdn/cdn-do.ts | 9 ++------- 2 files changed, 7 insertions(+), 32 deletions(-) diff --git a/src/auth/auth-do.ts b/src/auth/auth-do.ts index a5be738..a2019eb 100644 --- a/src/auth/auth-do.ts +++ b/src/auth/auth-do.ts @@ -77,7 +77,7 @@ export class AuthDO extends DurableObject { // get signature from body const body = await request.json(); if (!body || typeof body !== 'object' || !('signature' in body) || !('publicKey' in body)) { - return createJsonResponse('Missing or invalid "signature" or "publicKey" in request body', 400); + return createJsonResponse('Missing required parameters: signature, publicKey', 400); } const signedMessage = String(body.signature); const publicKey = String(body.publicKey); @@ -142,32 +142,17 @@ export class AuthDO extends DurableObject { // get address from body const body = await request.json(); if (!body || typeof body !== 'object' || !('data' in body)) { - return createJsonResponse( - { - error: 'Missing or invalid "data" in request body', - }, - 400 - ); + return createJsonResponse('Missing required parameter: data', 400); } const address = String(body.data); const validAddress = validateStacksAddress(address); if (!validAddress) { - return createJsonResponse( - { - error: `Invalid address ${address}`, - }, - 400 - ); + return createJsonResponse(`Invalid address: ${address}`, 400); } // get session key from kv key list const sessionKey = await this.env.AIBTCDEV_SERVICES_KV.get(`${this.KEY_PREFIX}:address:${address}`); if (sessionKey === null) { - return createJsonResponse( - { - error: `Address ${address} not found in key list`, - }, - 401 - ); + return createJsonResponse(`Address not found: ${address}`, 401); } // return 200 with session info return createJsonResponse({ @@ -192,12 +177,7 @@ export class AuthDO extends DurableObject { // get address from kv key list const address = await this.env.AIBTCDEV_SERVICES_KV.get(`${this.KEY_PREFIX}:session:${sessionToken}`); if (address === null) { - return createJsonResponse( - { - error: `Session key ${sessionToken} not found in key list`, - }, - 401 - ); + return createJsonResponse(`Invalid session token: ${sessionToken}`, 401); } // return 200 with session info return createJsonResponse({ diff --git a/src/cdn/cdn-do.ts b/src/cdn/cdn-do.ts index 8c7ab97..ce25d19 100644 --- a/src/cdn/cdn-do.ts +++ b/src/cdn/cdn-do.ts @@ -110,18 +110,13 @@ export class CdnDO extends DurableObject { } }); } catch (error) { - return createJsonResponse({ error: 'Failed to list objects' }, 500); + return createJsonResponse('Failed to list objects', 500); } } // all methods from this point forward are POST if (request.method !== 'POST') { - return createJsonResponse( - { - error: `Unsupported method: ${request.method}, supported method: POST`, - }, - 405 - ); + return createJsonResponse(`Unsupported method: ${request.method}, supported method: POST`, 405); } if (endpoint === '/put') { From a11b30fadc7f09b3f9a808b22ac1261d00e1d395 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:14:11 -0700 Subject: [PATCH 012/128] refactor: Standardize endpoint responses in auth-do.ts --- src/auth/auth-do.ts | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/auth/auth-do.ts b/src/auth/auth-do.ts index a2019eb..fb595bb 100644 --- a/src/auth/auth-do.ts +++ b/src/auth/auth-do.ts @@ -124,17 +124,14 @@ export class AuthDO extends DurableObject { await Promise.all([savePubkey, saveSessionToken, saveAddress]); // return 200 with session token return createJsonResponse({ - message: 'auth token successfully created', - address: addressFromPubkey, - sessionToken, + message: 'Successfully created auth token', + data: { + address: addressFromPubkey, + sessionToken + } }); } catch (error) { - return createJsonResponse( - { - error: `Failed to verify signature ${signedMessage}: ${error instanceof Error ? error.message : String(error)}`, - }, - 401 - ); + return createJsonResponse(`Failed to verify signature: ${error instanceof Error ? error.message : String(error)}`, 401); } } @@ -156,9 +153,11 @@ export class AuthDO extends DurableObject { } // return 200 with session info return createJsonResponse({ - message: 'address successfully verified', - address, - sessionKey, + message: 'Successfully verified address', + data: { + address, + sessionKey + } }); } @@ -181,9 +180,11 @@ export class AuthDO extends DurableObject { } // return 200 with session info return createJsonResponse({ - message: 'session token successfully verified', - address, - sessionToken, + message: 'Successfully verified session token', + data: { + address, + sessionToken + } }); } From a3564a50673d8e3ca636284e06961a6379c4a2f8 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:17:42 -0700 Subject: [PATCH 013/128] refactor: Standardize response formats in auth and cdn endpoints --- src/auth/auth-do.ts | 7 +------ src/cdn/cdn-do.ts | 7 +++++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/auth/auth-do.ts b/src/auth/auth-do.ts index fb595bb..a19219c 100644 --- a/src/auth/auth-do.ts +++ b/src/auth/auth-do.ts @@ -165,12 +165,7 @@ export class AuthDO extends DurableObject { // get session key from body const body = await request.json(); if (!body || typeof body !== 'object' || !('data' in body)) { - return createJsonResponse( - { - error: 'Missing or invalid "data" in request body', - }, - 400 - ); + return createJsonResponse('Missing or invalid "data" in request body', 400); } const sessionToken = String(body.data); // get address from kv key list diff --git a/src/cdn/cdn-do.ts b/src/cdn/cdn-do.ts index ce25d19..a15be62 100644 --- a/src/cdn/cdn-do.ts +++ b/src/cdn/cdn-do.ts @@ -51,7 +51,10 @@ export class CdnDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { return createJsonResponse({ - message: `Supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, + message: 'Welcome to CDN service', + data: { + supportedEndpoints: this.SUPPORTED_ENDPOINTS + } }); } @@ -148,6 +151,6 @@ export class CdnDO extends DurableObject { } } - return createJsonResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); + return createJsonResponse(`Unsupported endpoint: ${endpoint}`, 404); } } From d483e35feb74c749b31a2ef721e462e288f1d672 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Thu, 19 Dec 2024 22:40:51 -0700 Subject: [PATCH 014/128] fix: adjust return type helper response --- src/utils/requests-responses.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/utils/requests-responses.ts b/src/utils/requests-responses.ts index ee84c4e..2807c48 100644 --- a/src/utils/requests-responses.ts +++ b/src/utils/requests-responses.ts @@ -1,3 +1,9 @@ +type ApiResponse = { + success: boolean; + data?: any; + error?: string; +}; + export function corsHeaders(origin?: string): HeadersInit { return { 'Access-Control-Allow-Origin': origin || '*', @@ -7,8 +13,17 @@ export function corsHeaders(origin?: string): HeadersInit { }; } -export function createJsonResponse(body: unknown, status = 200): Response { - return new Response(typeof body === 'string' ? body : JSON.stringify(body), { +export function createApiResponse(response: { message: string; data?: unknown } | string, status: number = 200): Response { + const isOk = status >= 200 && status < 300; + const responseBody: ApiResponse = { + success: isOk, + ...(isOk + ? { + data: typeof response === 'string' ? { message: response } : { message: response.message, ...response.data }, + } + : { error: response as string }), + }; + return new Response(JSON.stringify(responseBody), { status, headers: { 'Content-Type': 'application/json', From c6ddf99c19b86ac0d2b0e9cb46abd2681383e6b6 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:42:33 -0700 Subject: [PATCH 015/128] docs: Update consistent endpoints documentation for createApiResponse --- src/consistent_endpoints.md | 58 +++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/src/consistent_endpoints.md b/src/consistent_endpoints.md index e46872e..9a80d73 100644 --- a/src/consistent_endpoints.md +++ b/src/consistent_endpoints.md @@ -1,6 +1,6 @@ -# Implementation Guide: createJsonResponse +# Implementation Guide: createApiResponse -This guide outlines the standardized implementation of `createJsonResponse` across all endpoints. +This guide outlines the standardized implementation of `createApiResponse` across all endpoints. ## Response Formats @@ -9,17 +9,17 @@ This guide outlines the standardized implementation of `createJsonResponse` acro #### When Returning Data ```javascript -createJsonResponse({ - message: 'A clear description of what succeeded', - data: theActualData, +createApiResponse({ + message: 'A clear description of what succeeded', + data: { theActualData } }); ``` #### When Returning Message Only ```javascript -createJsonResponse({ - message: 'A clear description of what succeeded', +createApiResponse({ + message: 'A clear description of what succeeded' }); ``` @@ -28,48 +28,70 @@ createJsonResponse({ Use a direct string message: ```javascript -createJsonResponse('A clear description of what went wrong', errorStatusCode); +createApiResponse('A clear description of what went wrong', errorStatusCode); ``` ## Implementation Guidelines ### 3. Specific Patterns -- Never wrap response data in unnecessary object literals (e.g., avoid `{ result }` when `result` is already an object) - Always include a descriptive message for successful operations - Keep error messages concise and descriptive - Use consistent status codes for similar types of errors +- Data should be structured as an object when including additional information ### 4. Implementation Examples #### Success with Data ```javascript -return createJsonResponse({ - message: 'Successfully retrieved crews', - data: crews, +return createApiResponse({ + message: 'Successfully retrieved crews', + data: { crews } }); ``` #### Success with Message Only ```javascript -return createJsonResponse({ - message: `Successfully deleted ${key}`, +return createApiResponse({ + message: `Successfully deleted ${key}` }); ``` #### Error Response ```javascript -return createJsonResponse('Missing required parameter: address', 400); +return createApiResponse('Missing required parameter: address', 400); ``` ## Response Structure -The `createJsonResponse` function automatically wraps these responses in a standardized format that includes: +The `createApiResponse` function automatically wraps responses in a standardized format that includes: + +- A success boolean flag (true for 200-299 status codes, false otherwise) +- For successful responses: + - data object containing message and any additional data +- For error responses: + - error string with the error message + +Example Success Response: +```json +{ + "success": true, + "data": { + "message": "Successfully retrieved crews", + "crews": [...] + } +} +``` -- A success boolean flag -- Either a data or error property based on the status code +Example Error Response: +```json +{ + "success": false, + "error": "Missing required parameter: address" +} +``` This standardization ensures consistent response structures across all endpoints and makes it easier for clients to handle responses predictably. From 47b88815c94a8acfc2d03f2d7e036e3547af26dc Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:44:39 -0700 Subject: [PATCH 016/128] refactor: Replace createJsonResponse with createApiResponse in auth-do.ts --- src/auth/auth-do.ts | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/auth/auth-do.ts b/src/auth/auth-do.ts index a19219c..beb9741 100644 --- a/src/auth/auth-do.ts +++ b/src/auth/auth-do.ts @@ -3,7 +3,7 @@ import { verifyMessageSignatureRsv } from '@stacks/encryption'; import { getAddressFromPublicKey, validateStacksAddress } from '@stacks/transactions'; import { Env } from '../../worker-configuration'; import { AppConfig } from '../config'; -import { createJsonResponse } from '../utils/requests-responses'; +import { createApiResponse } from '../utils/requests-responses'; import { validateSharedKeyAuth } from '../utils/auth-helper'; /** @@ -48,7 +48,7 @@ export class AuthDO extends DurableObject { const path = url.pathname; if (!path.startsWith(this.BASE_PATH)) { - return createJsonResponse(`Request at ${path} does not start with base path ${this.BASE_PATH}`, 404); + return createApiResponse(`Request at ${path} does not start with base path ${this.BASE_PATH}`, 404); } // Remove base path to get the endpoint @@ -56,7 +56,7 @@ export class AuthDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { - return createJsonResponse({ + return createApiResponse({ message: `Supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, }); } @@ -65,19 +65,19 @@ export class AuthDO extends DurableObject { // frontend and backend each have their own stored in KV const authResult = await validateSharedKeyAuth(this.env, request); if (!authResult.success) { - return createJsonResponse({ error: authResult.error }, authResult.status); + return createApiResponse(authResult.error, authResult.status); } // all methods from this point forward are POST if (request.method !== 'POST') { - return createJsonResponse(`Unsupported method: ${request.method}, supported method: POST`, 405); + return createApiResponse(`Unsupported method: ${request.method}, supported method: POST`, 405); } if (endpoint === '/request-auth-token') { // get signature from body const body = await request.json(); if (!body || typeof body !== 'object' || !('signature' in body) || !('publicKey' in body)) { - return createJsonResponse('Missing required parameters: signature, publicKey', 400); + return createApiResponse('Missing required parameters: signature, publicKey', 400); } const signedMessage = String(body.signature); const publicKey = String(body.publicKey); @@ -93,14 +93,12 @@ export class AuthDO extends DurableObject { const isAddressValid = validateStacksAddress(addressFromPubkey); // check if signature is valid with the public key if (!isSignatureVerified) { - return createJsonResponse(`Signature verification failed for public key ${publicKey}`, 401); + return createApiResponse(`Signature verification failed for public key ${publicKey}`, 401); } // check if address is valid if (!isAddressValid) { - return createJsonResponse( - { - error: `Invalid address ${addressFromPubkey} from public key ${publicKey}`, - }, + return createApiResponse( + `Invalid address ${addressFromPubkey} from public key ${publicKey}`, 400 ); } @@ -123,7 +121,7 @@ export class AuthDO extends DurableObject { // wait for all kv operations to complete await Promise.all([savePubkey, saveSessionToken, saveAddress]); // return 200 with session token - return createJsonResponse({ + return createApiResponse({ message: 'Successfully created auth token', data: { address: addressFromPubkey, @@ -131,7 +129,7 @@ export class AuthDO extends DurableObject { } }); } catch (error) { - return createJsonResponse(`Failed to verify signature: ${error instanceof Error ? error.message : String(error)}`, 401); + return createApiResponse(`Failed to verify signature: ${error instanceof Error ? error.message : String(error)}`, 401); } } @@ -139,20 +137,20 @@ export class AuthDO extends DurableObject { // get address from body const body = await request.json(); if (!body || typeof body !== 'object' || !('data' in body)) { - return createJsonResponse('Missing required parameter: data', 400); + return createApiResponse('Missing required parameter: data', 400); } const address = String(body.data); const validAddress = validateStacksAddress(address); if (!validAddress) { - return createJsonResponse(`Invalid address: ${address}`, 400); + return createApiResponse(`Invalid address: ${address}`, 400); } // get session key from kv key list const sessionKey = await this.env.AIBTCDEV_SERVICES_KV.get(`${this.KEY_PREFIX}:address:${address}`); if (sessionKey === null) { - return createJsonResponse(`Address not found: ${address}`, 401); + return createApiResponse(`Address not found: ${address}`, 401); } // return 200 with session info - return createJsonResponse({ + return createApiResponse({ message: 'Successfully verified address', data: { address, @@ -165,16 +163,16 @@ export class AuthDO extends DurableObject { // get session key from body const body = await request.json(); if (!body || typeof body !== 'object' || !('data' in body)) { - return createJsonResponse('Missing or invalid "data" in request body', 400); + return createApiResponse('Missing or invalid "data" in request body', 400); } const sessionToken = String(body.data); // get address from kv key list const address = await this.env.AIBTCDEV_SERVICES_KV.get(`${this.KEY_PREFIX}:session:${sessionToken}`); if (address === null) { - return createJsonResponse(`Invalid session token: ${sessionToken}`, 401); + return createApiResponse(`Invalid session token: ${sessionToken}`, 401); } // return 200 with session info - return createJsonResponse({ + return createApiResponse({ message: 'Successfully verified session token', data: { address, @@ -185,6 +183,6 @@ export class AuthDO extends DurableObject { // TODO: endpoint to revoke a session token - return createJsonResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); + return createApiResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); } } From 635407e3f9088de54db66e8613b20b9d5d454967 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:45:23 -0700 Subject: [PATCH 017/128] refactor: Update cdn-do.ts to use createApiResponse consistently --- src/cdn/cdn-do.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cdn/cdn-do.ts b/src/cdn/cdn-do.ts index a15be62..2f6cfd7 100644 --- a/src/cdn/cdn-do.ts +++ b/src/cdn/cdn-do.ts @@ -1,6 +1,6 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; -import { createJsonResponse } from '../utils/requests-responses'; +import { createApiResponse } from '../utils/requests-responses'; import { validateSharedKeyAuth } from '../utils/auth-helper'; import { AppConfig } from '../config'; @@ -42,7 +42,7 @@ export class CdnDO extends DurableObject { const path = url.pathname; if (!path.startsWith(this.BASE_PATH)) { - return createJsonResponse(`Request at ${path} does not start with base path ${this.BASE_PATH}`, 404); + return createApiResponse(`Request at ${path} does not start with base path ${this.BASE_PATH}`, 404); } // Remove base path to get the endpoint @@ -50,7 +50,7 @@ export class CdnDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { - return createJsonResponse({ + return createApiResponse({ message: 'Welcome to CDN service', data: { supportedEndpoints: this.SUPPORTED_ENDPOINTS @@ -66,7 +66,7 @@ export class CdnDO extends DurableObject { const object = await this.env.AIBTCDEV_SERVICES_BUCKET.get(r2ObjectKey); if (!object) { - return createJsonResponse('Object not found', 404); + return createApiResponse('Object not found', 404); } // Return the object with appropriate headers @@ -78,7 +78,7 @@ export class CdnDO extends DurableObject { }, }); } catch (error) { - return createJsonResponse('Failed to retrieve object', 500); + return createApiResponse('Failed to retrieve object', 500); } } @@ -86,7 +86,7 @@ export class CdnDO extends DurableObject { // frontend and backend each have their own stored in KV const authResult = await validateSharedKeyAuth(this.env, request); if (!authResult.success) { - return createJsonResponse({ error: authResult.error }, authResult.status); + return createApiResponse(authResult.error, authResult.status); } if (endpoint === '/list') { @@ -98,7 +98,7 @@ export class CdnDO extends DurableObject { }; const objects = await this.env.AIBTCDEV_SERVICES_BUCKET.list(options); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully listed objects', data: { objects: objects.objects.map((obj) => ({ @@ -109,17 +109,17 @@ export class CdnDO extends DurableObject { httpEtag: obj.httpEtag, })), truncated: objects.truncated, - cursor: objects.truncated ? objects.cursor : undefined, + cursor: objects.truncated ? objects.cursor : undefined } }); } catch (error) { - return createJsonResponse('Failed to list objects', 500); + return createApiResponse('Failed to list objects', 500); } } // all methods from this point forward are POST if (request.method !== 'POST') { - return createJsonResponse(`Unsupported method: ${request.method}, supported method: POST`, 405); + return createApiResponse(`Unsupported method: ${request.method}, supported method: POST`, 405); } if (endpoint === '/put') { @@ -130,27 +130,27 @@ export class CdnDO extends DurableObject { }, }); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully stored object', data: { r2ObjectKey, etag: object.httpEtag } }); } catch (error) { - return createJsonResponse('Failed to store object', 500); + return createApiResponse('Failed to store object', 500); } } if (endpoint === '/delete') { try { await this.env.AIBTCDEV_SERVICES_BUCKET.delete(r2ObjectKey); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully deleted object', data: { r2ObjectKey } }); } catch (error) { - return createJsonResponse('Failed to delete object', 500); + return createApiResponse('Failed to delete object', 500); } } - return createJsonResponse(`Unsupported endpoint: ${endpoint}`, 404); + return createApiResponse(`Unsupported endpoint: ${endpoint}`, 404); } } From 24b8618a4a465a7f4b920dacf6a200e5f7a93262 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:46:20 -0700 Subject: [PATCH 018/128] refactor: Update database-do.ts to use createApiResponse consistently --- src/database/database-do.ts | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 89c4e2f..510cc31 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -1,7 +1,7 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; import { AppConfig } from '../config'; -import { createJsonResponse } from '../utils/requests-responses'; +import { createApiResponse } from '../utils/requests-responses'; import { D1Orm } from 'd1-orm'; import { UserAgentsTable, @@ -137,12 +137,7 @@ export class DatabaseDO extends DurableObject { const path = url.pathname; if (!path.startsWith(this.BASE_PATH)) { - return createJsonResponse( - { - error: `Request at ${path} does not start with base path ${this.BASE_PATH}`, - }, - 404 - ); + return createApiResponse(`Request at ${path} does not start with base path ${this.BASE_PATH}`, 404); } // Remove base path to get the endpoint @@ -150,7 +145,7 @@ export class DatabaseDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { - return createJsonResponse({ + return createApiResponse({ message: 'Database service endpoints available', data: { endpoints: this.SUPPORTED_ENDPOINTS @@ -162,7 +157,7 @@ export class DatabaseDO extends DurableObject { // frontend and backend each have their own stored in KV const authResult = await validateSharedKeyAuth(this.env, request); if (!authResult.success) { - return createJsonResponse(authResult.error, authResult.status); + return createApiResponse(authResult.error, authResult.status); } try { @@ -170,10 +165,10 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/conversations') { const address = url.searchParams.get('address'); if (!address) { - return createJsonResponse('Missing address parameter', 400); + return createApiResponse('Missing address parameter', 400); } const conversations = await getConversations(this.orm, address); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved conversations', data: conversations }); @@ -660,7 +655,7 @@ export class DatabaseDO extends DurableObject { } } catch (error) { console.error(`Database error: ${error instanceof Error ? error.message : String(error)}`); - return createJsonResponse({ error: `Database error: ${error instanceof Error ? error.message : String(error)}` }, 500); + return createApiResponse(`Database error: ${error instanceof Error ? error.message : String(error)}`, 500); } // Twitter/X Bot endpoints @@ -857,10 +852,8 @@ export class DatabaseDO extends DurableObject { return createJsonResponse({ result }); } - return createJsonResponse( - { - error: `Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, - }, + return createApiResponse( + `Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404 ); } From eda8a203f13c10a14e55c2afe4145d6ffe6d5ea3 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:47:49 -0700 Subject: [PATCH 019/128] refactor: Replace createJsonResponse with createApiResponse across endpoints --- src/database/database-do.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 510cc31..0519c41 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -177,10 +177,10 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/conversations/latest') { const address = url.searchParams.get('address'); if (!address) { - return createJsonResponse('Missing address parameter', 400); + return createApiResponse('Missing address parameter', 400); } const conversation = await getLatestConversation(this.orm, address); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved latest conversation', data: conversation }); @@ -189,10 +189,10 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/conversations/history') { const conversationId = url.searchParams.get('id'); if (!conversationId) { - return createJsonResponse('Missing conversation ID parameter', 400); + return createApiResponse('Missing conversation ID parameter', 400); } const history = await getConversationHistory(this.orm, parseInt(conversationId)); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved conversation history', data: history }); @@ -208,7 +208,7 @@ export class DatabaseDO extends DurableObject { // Get the session token from Authorization header const authHeader = request.headers.get('Authorization'); if (!authHeader) { - return createJsonResponse('Missing authorization header', 401); + return createApiResponse('Missing authorization header', 401); } // Extract token from Bearer format @@ -217,11 +217,11 @@ export class DatabaseDO extends DurableObject { // Verify the token matches the requested address const tokenAddress = await validateSessionToken(this.env, token); if (!tokenAddress.success || tokenAddress.address !== address) { - return createJsonResponse('Unauthorized access', 403); + return createApiResponse('Unauthorized access', 403); } const crews = await getCrewsByProfile(this.orm, address); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved profile crews', data: crews }); From 91ea10a83c9108d1731e92aa86247d181853bc12 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:48:52 -0700 Subject: [PATCH 020/128] refactor: Update endpoints to use createApiResponse consistently --- src/database/database-do.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 0519c41..1acf998 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -662,7 +662,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/twitter/authors/get') { const authorId = url.searchParams.get('authorId'); if (!authorId) { - return createJsonResponse('Missing authorId parameter', 400); + return createApiResponse('Missing authorId parameter', 400); } const author = await getAuthor(this.orm, authorId); return createJsonResponse({ @@ -673,14 +673,17 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/twitter/authors/create') { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } const { author_id, realname, username } = (await request.json()) as XBotAuthorsTable; if (!author_id) { - return createJsonResponse({ error: 'Missing required fields: authorId' }, 400); + return createApiResponse('Missing required fields: authorId', 400); } const author = await addAuthor(this.orm, author_id, realname || undefined, username || undefined); - return createJsonResponse({ author }); + return createApiResponse({ + message: 'Successfully created author', + data: { author } + }); } if (endpoint === '/twitter/tweets/get') { @@ -736,7 +739,10 @@ export class DatabaseDO extends DurableObject { parent_tweet_id || undefined, is_bot_response || undefined ); - return createJsonResponse({ tweet }); + return createApiResponse({ + message: 'Successfully created tweet', + data: { tweet } + }); } if (endpoint === '/twitter/logs/get') { From 5cd81157c08ad091eda166bdfd87132f53ee62c3 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:49:09 -0700 Subject: [PATCH 021/128] feat: Update cron endpoints to use createApiResponse consistently --- src/database/database-do.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 1acf998..a55016a 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -499,22 +499,31 @@ export class DatabaseDO extends DurableObject { // Cron endpoints if (endpoint === '/crons/enabled') { const crons = await getEnabledCrons(this.orm); - return createJsonResponse({ crons }); + return createApiResponse({ + message: 'Successfully retrieved enabled crons', + data: { crons } + }); } if (endpoint === '/crons/enabled/detailed') { const crons = await getEnabledCronsDetailed(this.orm); - return createJsonResponse({ crons }); + return createApiResponse({ + message: 'Successfully retrieved detailed cron information', + data: { crons } + }); } // Cron management endpoints if (endpoint === '/crons/get') { const crewId = url.searchParams.get('crewId'); if (!crewId) { - return createJsonResponse({ error: 'Missing crewId parameter' }, 400); + return createApiResponse('Missing crewId parameter', 400); } const crons = await getCronsByCrew(this.orm, parseInt(crewId)); - return createJsonResponse({ crons }); + return createApiResponse({ + message: 'Successfully retrieved crons for crew', + data: { crons } + }); } if (endpoint === '/crons/create') { From 1cff3fc6e6dcea50f46e9deb868f22c67d6e46eb Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:51:10 -0700 Subject: [PATCH 022/128] refactor: Update profile endpoints to use createApiResponse consistently --- src/database/database-do.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index a55016a..daac7e8 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -592,10 +592,13 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/profiles/role') { const address = url.searchParams.get('address'); if (!address) { - return createJsonResponse({ error: 'Missing address parameter' }, 400); + return createApiResponse('Missing address parameter', 400); } const role = await getUserRole(this.orm, address); - return createJsonResponse({ role }); + return createApiResponse({ + message: 'Successfully retrieved user role', + data: { role } + }); } if (endpoint === '/profiles/get') { @@ -604,7 +607,10 @@ export class DatabaseDO extends DurableObject { return createJsonResponse({ error: 'Missing address parameter' }, 400); } const profile = await getUserProfile(this.orm, address); - return createJsonResponse({ profile }); + return createApiResponse({ + message: 'Successfully retrieved user profile', + data: { profile } + }); } if (endpoint === '/profiles/create') { @@ -613,7 +619,7 @@ export class DatabaseDO extends DurableObject { } const profileData = (await request.json()) as UserProfilesTable; if (!profileData.stx_address || !profileData.user_role) { - return createJsonResponse({ error: 'Missing required fields: stx_address, user_role' }, 400); + return createApiResponse('Missing required fields: stx_address, user_role', 400); } const profile = await createUserProfile(this.orm, profileData); return createJsonResponse({ profile }); From eb601d904c1fc124cf65ed2d1616617d132b1a71 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:51:27 -0700 Subject: [PATCH 023/128] refactor: Update endpoints to use createApiResponse consistently --- src/database/database-do.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index daac7e8..9005e7b 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -554,7 +554,10 @@ export class DatabaseDO extends DurableObject { return createJsonResponse({ error: 'Missing cron_input in request body' }, 400); } const result = await updateCronInput(this.orm, parseInt(cronId), cron_input); - return createJsonResponse({ result }); + return createApiResponse({ + message: 'Successfully updated user profile', + data: { result } + }); } if (endpoint === '/crons/toggle') { @@ -653,7 +656,10 @@ export class DatabaseDO extends DurableObject { // Admin profile endpoints if (endpoint === '/profiles/admin/list') { const profiles = await getAllUserProfiles(this.orm); - return createJsonResponse({ profiles }); + return createApiResponse({ + message: 'Successfully retrieved all user profiles', + data: { profiles } + }); } if (endpoint === '/profiles/admin/update') { @@ -662,7 +668,7 @@ export class DatabaseDO extends DurableObject { } const userId = url.searchParams.get('userId'); if (!userId) { - return createJsonResponse({ error: 'Missing userId parameter' }, 400); + return createApiResponse('Missing userId parameter', 400); } const updates = (await request.json()) as UserProfilesTable; const result = await updateUserProfileById(this.orm, parseInt(userId), updates); @@ -739,7 +745,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/twitter/tweets/add') { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } const { author_id, tweet_id, tweet_body, thread_id, parent_tweet_id, is_bot_response } = (await request.json()) as XBotTweetsTable; if (!author_id || !tweet_id || !tweet_body) { From 3bc2b102c88b2f640478119d781835741b4e9511 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:51:41 -0700 Subject: [PATCH 024/128] refactor: Update endpoints to use createApiResponse consistently --- src/database/database-do.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 9005e7b..3139a3a 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -686,9 +686,9 @@ export class DatabaseDO extends DurableObject { return createApiResponse('Missing authorId parameter', 400); } const author = await getAuthor(this.orm, authorId); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved author', - data: author + data: { author } }); } @@ -710,12 +710,12 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/twitter/tweets/get') { const tweetId = url.searchParams.get('tweetId'); if (!tweetId) { - return createJsonResponse('Missing tweetId parameter', 400); + return createApiResponse('Missing tweetId parameter', 400); } const tweet = await getTweet(this.orm, tweetId); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved tweet', - data: tweet + data: { tweet } }); } @@ -725,9 +725,9 @@ export class DatabaseDO extends DurableObject { return createJsonResponse('Missing threadId parameter', 400); } const tweets = await getThreadTweets(this.orm, parseInt(threadId)); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved thread tweets', - data: tweets + data: { tweets } }); } From 0f16cfb369e72f44ded655384386ca526a2b24a7 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:51:58 -0700 Subject: [PATCH 025/128] feat: Refactor createApiResponse usage in database-do.ts endpoints --- src/database/database-do.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 3139a3a..0f6518e 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -493,7 +493,10 @@ export class DatabaseDO extends DurableObject { } const execution = await addCrewExecution(this.orm, address, crewId, conversationId, input); - return createJsonResponse({ execution }); + return createApiResponse({ + message: 'Successfully created crew execution', + data: { execution } + }); } // Cron endpoints @@ -780,7 +783,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/twitter/logs/add') { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } const { tweet_id, tweet_status, log_message } = (await request.json()) as XBotLogsTable; if (!tweet_id || !tweet_status) { From 70543a859b4c826e6c95077cdfaa22f67e2c6129 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:52:04 -0700 Subject: [PATCH 026/128] refactor: Replace createJsonResponse with createApiResponse for error handling --- src/database/database-do.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 0f6518e..a59ad35 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -484,12 +484,7 @@ export class DatabaseDO extends DurableObject { const { address, crewId, conversationId, input } = body; if (!address || !crewId || !conversationId || !input) { - return createJsonResponse( - { - error: 'Missing required parameters: address, crewId, conversationId, input', - }, - 400 - ); + return createApiResponse('Missing required parameters: address, crewId, conversationId, input', 400); } const execution = await addCrewExecution(this.orm, address, crewId, conversationId, input); From 0b223349a57d4dd77f764fb4bab50ea616989c7b Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:53:26 -0700 Subject: [PATCH 027/128] refactor: Update endpoints to use createApiResponse consistently --- src/database/database-do.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index a59ad35..a831df0 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -536,7 +536,10 @@ export class DatabaseDO extends DurableObject { cronData.cron_interval = cronData.cron_interval || '0 * * * *'; // Default to hourly cronData.cron_input = cronData.cron_input || ''; const cron = await createCron(this.orm, cronData); - return createJsonResponse({ cron }); + return createApiResponse({ + message: 'Successfully created cron', + data: { cron } + }); } if (endpoint === '/crons/update') { @@ -568,10 +571,13 @@ export class DatabaseDO extends DurableObject { } const { cron_enabled } = (await request.json()) as UserCronsTable; if (cron_enabled === undefined) { - return createJsonResponse({ error: 'Missing enabled in request body' }, 400); + return createApiResponse('Missing cron_enabled in request body', 400); } const result = await toggleCronStatus(this.orm, parseInt(cronId), cron_enabled ? 1 : 0); - return createJsonResponse({ result }); + return createApiResponse({ + message: 'Successfully toggled cron status', + data: { result } + }); } // Conversation creation endpoint @@ -836,7 +842,7 @@ export class DatabaseDO extends DurableObject { const stepData = (await request.json()) as UserCrewExecutionStepsTable; if (!stepData.profile_id || !stepData.crew_id || !stepData.execution_id || !stepData.step_type || !stepData.step_data) { - return createJsonResponse({ error: 'Missing required fields' }, 400); + return createApiResponse('Missing required fields: profile_id, crew_id, cron_enabled', 400); } // Verify the profile_id matches the token address From 025278fe568dae4fe9c854191d9f4dea129424c1 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:53:37 -0700 Subject: [PATCH 028/128] feat: Update database-do.ts to use createApiResponse consistently --- src/database/database-do.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index a831df0..7200f57 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -611,7 +611,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/profiles/get') { const address = url.searchParams.get('address'); if (!address) { - return createJsonResponse({ error: 'Missing address parameter' }, 400); + return createApiResponse('Missing address parameter', 400); } const profile = await getUserProfile(this.orm, address); return createApiResponse({ From 19a8a3092a6f575baf000237415dd83417c6a990 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:54:50 -0700 Subject: [PATCH 029/128] refactor: Update endpoints to use createApiResponse with consistent response structure --- src/database/database-do.ts | 43 ++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 7200f57..2349a0b 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -202,7 +202,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/crews/profile') { const address = url.searchParams.get('address'); if (!address) { - return createJsonResponse('Missing address parameter', 400); + return createApiResponse('Missing address parameter', 400); } // Get the session token from Authorization header @@ -229,36 +229,36 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/crews/public') { const crews = await getPublicCrews(this.orm); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved public crews', - data: crews + data: { crews } }); } if (endpoint === '/crews/get') { const crewId = url.searchParams.get('id'); if (!crewId) { - return createJsonResponse('Missing id parameter', 400); + return createApiResponse('Missing id parameter', 400); } const crew = await getCrew(this.orm, parseInt(crewId)); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved crew', - data: crew + data: { crew } }); } if (endpoint === '/crews/create') { if (request.method !== 'POST') { - return createJsonResponse('Method not allowed', 405); + return createApiResponse('Method not allowed', 405); } const crewData = (await request.json()) as Omit; if (!crewData.profile_id || !crewData.crew_name) { - return createJsonResponse('Missing required fields: profile_id, crew_name', 400); + return createApiResponse('Missing required fields: profile_id, crew_name', 400); } const crew = await createCrew(this.orm, crewData); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully created crew', - data: crew + data: { crew } }); } @@ -272,9 +272,9 @@ export class DatabaseDO extends DurableObject { } const updates = (await request.json()) as Partial>; const result = await updateCrew(this.orm, parseInt(crewId), updates); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully updated crew', - data: result + data: { result } }); } @@ -287,9 +287,9 @@ export class DatabaseDO extends DurableObject { return createJsonResponse('Missing id parameter', 400); } const result = await deleteCrew(this.orm, parseInt(crewId)); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully deleted crew', - data: result + data: { result } }); } @@ -592,7 +592,10 @@ export class DatabaseDO extends DurableObject { } const result = await addConversation(this.orm, profile_id, conversation_name ? conversation_name : 'new conversation'); - return createJsonResponse({ result }); + return createApiResponse({ + message: 'Successfully updated user profile', + data: { result } + }); } // Profile endpoints @@ -629,7 +632,10 @@ export class DatabaseDO extends DurableObject { return createApiResponse('Missing required fields: stx_address, user_role', 400); } const profile = await createUserProfile(this.orm, profileData); - return createJsonResponse({ profile }); + return createApiResponse({ + message: 'Successfully created user profile', + data: { profile } + }); } if (endpoint === '/profiles/update') { @@ -817,7 +823,10 @@ export class DatabaseDO extends DurableObject { } const steps = await getExecutionSteps(this.orm, parseInt(executionId)); - return createJsonResponse({ steps }); + return createApiResponse({ + message: 'Successfully retrieved execution steps', + data: { steps } + }); } if (endpoint === '/crews/steps/create') { From 47d11fe0b6011ed78e5be5504b4bfc91d7cb7150 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:54:55 -0700 Subject: [PATCH 030/128] refactor: Wrap crews data in object for consistent API response --- src/database/database-do.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 2349a0b..9deb3ff 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -223,7 +223,7 @@ export class DatabaseDO extends DurableObject { const crews = await getCrewsByProfile(this.orm, address); return createApiResponse({ message: 'Successfully retrieved profile crews', - data: crews + data: { crews } }); } From 69b568967b181880eef9c3c805314887d73543ad Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:55:10 -0700 Subject: [PATCH 031/128] refactor: Update endpoints to use createApiResponse consistently --- src/database/database-do.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 9deb3ff..6e7311a 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -470,7 +470,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/crews/executions/add') { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } type AddCrewExecutionRequest = { @@ -530,7 +530,7 @@ export class DatabaseDO extends DurableObject { } const cronData = (await request.json()) as UserCronsTable; if (!cronData.profile_id || !cronData.crew_id || cronData.cron_enabled === undefined) { - return createJsonResponse({ error: 'Missing required fields' }, 400); + return createApiResponse('Missing required fields: profile_id, crew_id, cron_enabled', 400); } // Set defaults if not provided cronData.cron_interval = cronData.cron_interval || '0 * * * *'; // Default to hourly @@ -648,7 +648,10 @@ export class DatabaseDO extends DurableObject { } const profileData = (await request.json()) as UserProfilesTable; const result = await updateUserProfile(this.orm, address, profileData); - return createJsonResponse({ result }); + return createApiResponse({ + message: 'Successfully created conversation', + data: { result } + }); } if (endpoint === '/profiles/delete') { @@ -831,7 +834,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/crews/steps/create') { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } // Get the session token from Authorization header From 799454054a82672627e93e133a6e55bfa310a1e6 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:55:22 -0700 Subject: [PATCH 032/128] refactor: Update error response method in database-do.ts --- src/database/database-do.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 6e7311a..1fa343b 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -588,7 +588,7 @@ export class DatabaseDO extends DurableObject { const { profile_id, conversation_name } = (await request.json()) as UserConversationsTable; if (!profile_id) { - return createJsonResponse({ error: 'Missing required field: address' }, 400); + return createApiResponse('Missing required field: address', 400); } const result = await addConversation(this.orm, profile_id, conversation_name ? conversation_name : 'new conversation'); From 4c4da93201f37577045de8d8bd9976ea511fa0db Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:55:42 -0700 Subject: [PATCH 033/128] refactor: Update endpoints to use createApiResponse consistently --- src/database/database-do.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 1fa343b..a2cfee4 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -264,11 +264,11 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/crews/update') { if (request.method !== 'PUT') { - return createJsonResponse('Method not allowed', 405); + return createApiResponse('Method not allowed', 405); } const crewId = url.searchParams.get('id'); if (!crewId) { - return createJsonResponse('Missing id parameter', 400); + return createApiResponse('Missing id parameter', 400); } const updates = (await request.json()) as Partial>; const result = await updateCrew(this.orm, parseInt(crewId), updates); @@ -280,11 +280,11 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/crews/delete') { if (request.method !== 'DELETE') { - return createJsonResponse('Method not allowed', 405); + return createApiResponse('Method not allowed', 405); } const crewId = url.searchParams.get('id'); if (!crewId) { - return createJsonResponse('Missing id parameter', 400); + return createApiResponse('Missing id parameter', 400); } const result = await deleteCrew(this.orm, parseInt(crewId)); return createApiResponse({ @@ -300,9 +300,9 @@ export class DatabaseDO extends DurableObject { return createJsonResponse('Missing crewId parameter', 400); } const agents = await getAgents(this.orm, parseInt(crewId)); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved agents', - data: agents + data: { agents } }); } From ec561d20ce6e1c7f9f14283589e4130e8384ce36 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:55:57 -0700 Subject: [PATCH 034/128] refactor: Update endpoints to use createApiResponse consistently --- src/database/database-do.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index a2cfee4..ea6d704 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -526,7 +526,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/crons/create') { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } const cronData = (await request.json()) as UserCronsTable; if (!cronData.profile_id || !cronData.crew_id || cronData.cron_enabled === undefined) { @@ -544,11 +544,11 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/crons/update') { if (request.method !== 'PUT') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } const cronId = url.searchParams.get('id'); if (!cronId) { - return createJsonResponse({ error: 'Missing id parameter' }, 400); + return createApiResponse('Missing id parameter', 400); } const { cron_input } = (await request.json()) as UserCronsTable; if (cron_input === undefined) { From 3c06d04fa813533bea419bc40f11d458bd070152 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:56:08 -0700 Subject: [PATCH 035/128] refactor: Replace createJsonResponse with createApiResponse in agents endpoint --- src/database/database-do.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index ea6d704..3070da8 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -297,11 +297,11 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/agents/get') { const crewId = url.searchParams.get('crewId'); if (!crewId) { - return createJsonResponse('Missing crewId parameter', 400); + return createApiResponse('Missing crewId parameter', 400); } const agents = await getAgents(this.orm, parseInt(crewId)); return createApiResponse({ - message: 'Successfully retrieved agents', + message: 'Successfully retrieved agents', data: { agents } }); } From 0ca7a88d0dc6d8e336a3382057cc25b99e788f5f Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:56:31 -0700 Subject: [PATCH 036/128] refactor: Standardize API response handling using createApiResponse --- src/database/database-do.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 3070da8..5eae12e 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -625,7 +625,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/profiles/create') { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } const profileData = (await request.json()) as UserProfilesTable; if (!profileData.stx_address || !profileData.user_role) { @@ -640,30 +640,33 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/profiles/update') { if (request.method !== 'PUT') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } const address = url.searchParams.get('address'); if (!address) { - return createJsonResponse({ error: 'Missing address parameter' }, 400); + return createApiResponse('Missing address parameter', 400); } const profileData = (await request.json()) as UserProfilesTable; const result = await updateUserProfile(this.orm, address, profileData); return createApiResponse({ - message: 'Successfully created conversation', + message: 'Successfully updated user profile', data: { result } }); } if (endpoint === '/profiles/delete') { if (request.method !== 'DELETE') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } const address = url.searchParams.get('address'); if (!address) { - return createJsonResponse({ error: 'Missing address parameter' }, 400); + return createApiResponse('Missing address parameter', 400); } const result = await deleteUserProfile(this.orm, address); - return createJsonResponse({ result }); + return createApiResponse({ + message: 'Successfully deleted user profile', + data: { result } + }); } // Admin profile endpoints From af9d3ea06d6456094672f367260b57c068ac80a5 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:57:30 -0700 Subject: [PATCH 037/128] refactor: Update endpoints to use createApiResponse consistently --- src/database/database-do.ts | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 5eae12e..907ec1a 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -308,7 +308,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/agents/create') { if (request.method !== 'POST') { - return createJsonResponse('Method not allowed', 405); + return createApiResponse('Method not allowed', 405); } const agentData = (await request.json()) as Omit; if ( @@ -319,45 +319,45 @@ export class DatabaseDO extends DurableObject { !agentData.agent_goal || !agentData.agent_backstory ) { - return createJsonResponse('Missing required fields: profile_id, crew_id, agent_name, agent_role, agent_goal, agent_backstory', 400); + return createApiResponse('Missing required fields: profile_id, crew_id, agent_name, agent_role, agent_goal, agent_backstory', 400); } const agent = await createAgent(this.orm, agentData); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully created agent', - data: agent + data: { agent } }); } if (endpoint === '/agents/update') { if (request.method !== 'PUT') { - return createJsonResponse('Method not allowed', 405); + return createApiResponse('Method not allowed', 405); } const agentId = url.searchParams.get('id'); if (!agentId) { - return createJsonResponse('Missing id parameter', 400); + return createApiResponse('Missing id parameter', 400); } const updates = (await request.json()) as Partial< Omit >; const result = await updateAgent(this.orm, parseInt(agentId), updates); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully updated agent', - data: result + data: { result } }); } if (endpoint === '/agents/delete') { if (request.method !== 'DELETE') { - return createJsonResponse('Method not allowed', 405); + return createApiResponse('Method not allowed', 405); } const agentId = url.searchParams.get('id'); if (!agentId) { - return createJsonResponse('Missing id parameter', 400); + return createApiResponse('Missing id parameter', 400); } const result = await deleteAgent(this.orm, parseInt(agentId)); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully deleted agent', - data: result + data: { result } }); } @@ -365,24 +365,24 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/tasks/get') { const taskId = url.searchParams.get('id'); if (!taskId) { - return createJsonResponse('Missing id parameter', 400); + return createApiResponse('Missing id parameter', 400); } const task = await getTask(this.orm, parseInt(taskId)); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved task', - data: task + data: { task } }); } if (endpoint === '/tasks/list') { const agentId = url.searchParams.get('agentId'); if (!agentId) { - return createJsonResponse('Missing agentId parameter', 400); + return createApiResponse('Missing agentId parameter', 400); } const tasks = await getTasks(this.orm, parseInt(agentId)); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved tasks', - data: tasks + data: { tasks } }); } From ff47b88e7e768fc78cd9538dfc0019f1fac7d2b8 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:59:02 -0700 Subject: [PATCH 038/128] refactor: Update endpoints to use createApiResponse consistently --- src/database/database-do.ts | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 907ec1a..ba7e24d 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -750,7 +750,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/twitter/tweets/author') { const authorId = url.searchParams.get('authorId'); if (!authorId) { - return createJsonResponse('Missing authorId parameter', 400); + return createApiResponse('Missing authorId parameter', 400); } const tweets = await getAuthorTweets(this.orm, authorId); return createJsonResponse({ @@ -765,7 +765,7 @@ export class DatabaseDO extends DurableObject { } const { author_id, tweet_id, tweet_body, thread_id, parent_tweet_id, is_bot_response } = (await request.json()) as XBotTweetsTable; if (!author_id || !tweet_id || !tweet_body) { - return createJsonResponse({ error: 'Missing required fields: authorId, tweetId, tweetBody' }, 400); + return createApiResponse('Missing required fields: authorId, tweetId, tweetBody', 400); } const tweet = await addTweet( this.orm, @@ -800,23 +800,26 @@ export class DatabaseDO extends DurableObject { } const { tweet_id, tweet_status, log_message } = (await request.json()) as XBotLogsTable; if (!tweet_id || !tweet_status) { - return createJsonResponse({ error: 'Missing required fields: tweetId, status' }, 400); + return createApiResponse('Missing required fields: tweetId, status', 400); } const log = await addLog(this.orm, tweet_id, tweet_status, log_message || undefined); - return createJsonResponse({ log }); + return createApiResponse({ + message: 'Successfully created log', + data: { log } + }); } // Crew execution steps endpoints if (endpoint === '/crews/steps/get') { const executionId = url.searchParams.get('executionId'); if (!executionId) { - return createJsonResponse({ error: 'Missing executionId parameter' }, 400); + return createApiResponse('Missing executionId parameter', 400); } // Get the session token from Authorization header const authHeader = request.headers.get('Authorization'); if (!authHeader) { - return createJsonResponse({ error: 'Missing authorization header' }, 401); + return createApiResponse('Missing authorization header', 401); } // Extract token from Bearer format @@ -825,7 +828,7 @@ export class DatabaseDO extends DurableObject { // Verify the token matches the requested address const tokenAddress = await validateSessionToken(this.env, token); if (!tokenAddress.success) { - return createJsonResponse({ error: 'Unauthorized access' }, 403); + return createApiResponse('Unauthorized access', 403); } const steps = await getExecutionSteps(this.orm, parseInt(executionId)); @@ -843,7 +846,7 @@ export class DatabaseDO extends DurableObject { // Get the session token from Authorization header const authHeader = request.headers.get('Authorization'); if (!authHeader) { - return createJsonResponse({ error: 'Missing authorization header' }, 401); + return createApiResponse('Missing authorization header', 401); } // Extract token from Bearer format @@ -852,7 +855,7 @@ export class DatabaseDO extends DurableObject { // Verify the token const tokenAddress = await validateSessionToken(this.env, token); if (!tokenAddress) { - return createJsonResponse({ error: 'Unauthorized access' }, 403); + return createApiResponse('Unauthorized access', 403); } const stepData = (await request.json()) as UserCrewExecutionStepsTable; @@ -866,17 +869,20 @@ export class DatabaseDO extends DurableObject { } const step = await createExecutionStep(this.orm, stepData); - return createJsonResponse({ step }); + return createApiResponse({ + message: 'Successfully created execution step', + data: { step } + }); } if (endpoint === '/crews/steps/delete') { if (request.method !== 'DELETE') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } const executionId = url.searchParams.get('executionId'); if (!executionId) { - return createJsonResponse({ error: 'Missing executionId parameter' }, 400); + return createApiResponse('Missing executionId parameter', 400); } // Get the session token from Authorization header @@ -895,7 +901,10 @@ export class DatabaseDO extends DurableObject { } const result = await deleteExecutionSteps(this.orm, parseInt(executionId)); - return createJsonResponse({ result }); + return createApiResponse({ + message: 'Successfully deleted execution steps', + data: { result } + }); } return createApiResponse( From 1de7244bdce3461ebc5d7e68e3c5d2887d0740ac Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:59:11 -0700 Subject: [PATCH 039/128] refactor: Replace createJsonResponse with createApiResponse and update response data structure --- src/database/database-do.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index ba7e24d..d30467c 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -753,9 +753,9 @@ export class DatabaseDO extends DurableObject { return createApiResponse('Missing authorId parameter', 400); } const tweets = await getAuthorTweets(this.orm, authorId); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved author tweets', - data: tweets + data: { tweets } }); } @@ -785,12 +785,12 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/twitter/logs/get') { const tweetId = url.searchParams.get('tweetId'); if (!tweetId) { - return createJsonResponse('Missing tweetId parameter', 400); + return createApiResponse('Missing tweetId parameter', 400); } const logs = await getTweetLogs(this.orm, tweetId); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved tweet logs', - data: logs + data: { logs } }); } From 6f2571bfbe8f5007b6f20af9d88c4e43dc6252f4 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:59:27 -0700 Subject: [PATCH 040/128] refactor: Replace createJsonResponse with createApiResponse for thread endpoint --- src/database/database-do.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index d30467c..f9cdb3c 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -738,7 +738,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/twitter/tweets/thread') { const threadId = url.searchParams.get('threadId'); if (!threadId) { - return createJsonResponse('Missing threadId parameter', 400); + return createApiResponse('Missing threadId parameter', 400); } const tweets = await getThreadTweets(this.orm, parseInt(threadId)); return createApiResponse({ From cc576e415f4542a115c803cf1db4581fa91d1f1b Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 22:59:56 -0700 Subject: [PATCH 041/128] refactor: Update tasks endpoints to use createApiResponse consistently --- src/database/database-do.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index f9cdb3c..51736c1 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -388,7 +388,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/tasks/create') { if (request.method !== 'POST') { - return createJsonResponse('Method not allowed', 405); + return createApiResponse('Method not allowed', 405); } const taskData = (await request.json()) as UserTasksTable; if ( @@ -399,12 +399,12 @@ export class DatabaseDO extends DurableObject { !taskData.task_description || !taskData.task_expected_output ) { - return createJsonResponse('Missing required fields: profile_id, crew_id, agent_id, task_name, task_description, task_expected_output', 400); + return createApiResponse('Missing required fields: profile_id, crew_id, agent_id, task_name, task_description, task_expected_output', 400); } const task = await createTask(this.orm, taskData); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully created task', - data: task + data: { task } }); } From 6a76630a9e8970ce606f0a4a3673c08d6dd76f8b Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 23:00:10 -0700 Subject: [PATCH 042/128] refactor: Replace createJsonResponse with createApiResponse and modify response data structure --- src/database/database-do.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 51736c1..4942bce 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -410,49 +410,49 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/tasks/update') { if (request.method !== 'PUT') { - return createJsonResponse('Method not allowed', 405); + return createApiResponse('Method not allowed', 405); } const taskId = url.searchParams.get('id'); if (!taskId) { - return createJsonResponse('Missing id parameter', 400); + return createApiResponse('Missing id parameter', 400); } const updates = (await request.json()) as Partial< Omit >; const result = await updateTask(this.orm, parseInt(taskId), updates); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully updated task', - data: result + data: { result } }); } if (endpoint === '/tasks/delete') { if (request.method !== 'DELETE') { - return createJsonResponse('Method not allowed', 405); + return createApiResponse('Method not allowed', 405); } const taskId = url.searchParams.get('id'); if (!taskId) { - return createJsonResponse('Missing id parameter', 400); + return createApiResponse('Missing id parameter', 400); } const result = await deleteTask(this.orm, parseInt(taskId)); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully deleted task', - data: result + data: { result } }); } if (endpoint === '/tasks/delete-all') { if (request.method !== 'DELETE') { - return createJsonResponse('Method not allowed', 405); + return createApiResponse('Method not allowed', 405); } const agentId = url.searchParams.get('agentId'); if (!agentId) { - return createJsonResponse('Missing agentId parameter', 400); + return createApiResponse('Missing agentId parameter', 400); } const result = await deleteTasks(this.orm, parseInt(agentId)); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully deleted all tasks for agent', - data: result + data: { result } }); } From 66760164664b4d02b30322f3c9d2b61a23d9fde4 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 23:00:36 -0700 Subject: [PATCH 043/128] refactor: Standardize API responses using createApiResponse across endpoints --- src/database/database-do.ts | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 4942bce..0ccfdc9 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -462,9 +462,9 @@ export class DatabaseDO extends DurableObject { return createJsonResponse('Missing address parameter', 400); } const executions = await getCrewExecutions(this.orm, address); - return createJsonResponse({ + return createApiResponse({ message: 'Successfully retrieved crew executions', - data: executions + data: { executions } }); } @@ -552,7 +552,7 @@ export class DatabaseDO extends DurableObject { } const { cron_input } = (await request.json()) as UserCronsTable; if (cron_input === undefined) { - return createJsonResponse({ error: 'Missing cron_input in request body' }, 400); + return createApiResponse('Missing cron_input in request body', 400); } const result = await updateCronInput(this.orm, parseInt(cronId), cron_input); return createApiResponse({ @@ -563,11 +563,11 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/crons/toggle') { if (request.method !== 'PUT') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } const cronId = url.searchParams.get('id'); if (!cronId) { - return createJsonResponse({ error: 'Missing id parameter' }, 400); + return createApiResponse('Missing id parameter', 400); } const { cron_enabled } = (await request.json()) as UserCronsTable; if (cron_enabled === undefined) { @@ -680,7 +680,7 @@ export class DatabaseDO extends DurableObject { if (endpoint === '/profiles/admin/update') { if (request.method !== 'PUT') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } const userId = url.searchParams.get('userId'); if (!userId) { @@ -688,7 +688,10 @@ export class DatabaseDO extends DurableObject { } const updates = (await request.json()) as UserProfilesTable; const result = await updateUserProfileById(this.orm, parseInt(userId), updates); - return createJsonResponse({ result }); + return createApiResponse({ + message: 'Successfully updated user profile', + data: { result } + }); } } catch (error) { console.error(`Database error: ${error instanceof Error ? error.message : String(error)}`); @@ -865,7 +868,7 @@ export class DatabaseDO extends DurableObject { // Verify the profile_id matches the token address if (stepData.profile_id !== tokenAddress.address) { - return createJsonResponse({ error: 'Unauthorized: profile_id does not match token' }, 403); + return createApiResponse('Unauthorized: profile_id does not match token', 403); } const step = await createExecutionStep(this.orm, stepData); @@ -888,7 +891,7 @@ export class DatabaseDO extends DurableObject { // Get the session token from Authorization header const authHeader = request.headers.get('Authorization'); if (!authHeader) { - return createJsonResponse({ error: 'Missing authorization header' }, 401); + return createApiResponse('Missing authorization header', 401); } // Extract token from Bearer format @@ -897,7 +900,7 @@ export class DatabaseDO extends DurableObject { // Verify the token const tokenAddress = await validateSessionToken(this.env, token); if (!tokenAddress) { - return createJsonResponse({ error: 'Unauthorized access' }, 403); + return createApiResponse('Unauthorized access', 403); } const result = await deleteExecutionSteps(this.orm, parseInt(executionId)); From 940d9a85b3ba62401986a803d41f48e2415ac932 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 23:01:22 -0700 Subject: [PATCH 044/128] refactor: Update image-generator endpoints to use createApiResponse consistently --- src/image-generator/image-generator.do.ts | 52 +++++++++++------------ 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/image-generator/image-generator.do.ts b/src/image-generator/image-generator.do.ts index a9f9ec3..101a171 100644 --- a/src/image-generator/image-generator.do.ts +++ b/src/image-generator/image-generator.do.ts @@ -26,7 +26,7 @@ export class ImageGeneratorDO extends DurableObject { const path = url.pathname; if (!path.startsWith(this.BASE_PATH)) { - return createJsonResponse({ error: `Invalid base path: ${path}` }, 404); + return createApiResponse(`Invalid base path: ${path}`, 404); } const endpoint = path.replace(this.BASE_PATH, ''); @@ -34,12 +34,12 @@ export class ImageGeneratorDO extends DurableObject { // Require auth for all endpoints const authResult = await validateSharedKeyAuth(this.env, request); if (!authResult.success) { - return createJsonResponse({ error: authResult.error }, authResult.status); + return createApiResponse(authResult.error, authResult.status); } if (endpoint === '/generate') { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } try { @@ -50,7 +50,7 @@ export class ImageGeneratorDO extends DurableObject { } = (await request.json()) as { prompt: string; size?: string; n?: number }; if (!prompt) { - return createJsonResponse({ error: 'Prompt is required' }, 400); + return createApiResponse('Prompt is required', 400); } // Generate image with OpenAI @@ -89,18 +89,13 @@ export class ImageGeneratorDO extends DurableObject { }) ); - return createJsonResponse({ - success: true, - images: storedImages, + return createApiResponse({ + message: 'Successfully generated images', + data: { images: storedImages } }); } catch (error) { console.error('Image generation failed:', error); - return createJsonResponse( - { - error: 'Failed to generate or store image', - }, - 500 - ); + return createApiResponse('Failed to generate or store image', 500); } } @@ -110,7 +105,7 @@ export class ImageGeneratorDO extends DurableObject { const object = await this.env.AIBTCDEV_SERVICES_BUCKET.get(key); if (!object) { - return createJsonResponse({ error: 'Image not found' }, 404); + return createApiResponse('Image not found', 404); } return new Response(object.body, { @@ -120,7 +115,7 @@ export class ImageGeneratorDO extends DurableObject { }, }); } catch (error) { - return createJsonResponse({ error: 'Failed to retrieve image' }, 500); + return createApiResponse('Failed to retrieve image', 500); } } @@ -134,22 +129,25 @@ export class ImageGeneratorDO extends DurableObject { const objects = await this.env.AIBTCDEV_SERVICES_BUCKET.list(options); - return createJsonResponse({ - images: objects.objects.map((obj) => ({ - key: obj.key, - url: `/image/get/${obj.key}`, - size: obj.size, - uploaded: obj.uploaded, - metadata: obj.customMetadata, - })), - truncated: objects.truncated, - cursor: objects.truncated ? objects.cursor : undefined, + return createApiResponse({ + message: 'Successfully retrieved image list', + data: { + images: objects.objects.map((obj) => ({ + key: obj.key, + url: `/image/get/${obj.key}`, + size: obj.size, + uploaded: obj.uploaded, + metadata: obj.customMetadata, + })), + truncated: objects.truncated, + cursor: objects.truncated ? objects.cursor : undefined, + } }); } catch (error) { - return createJsonResponse({ error: 'Failed to list images' }, 500); + return createApiResponse('Failed to list images', 500); } } - return createJsonResponse({ error: `Unsupported endpoint: ${endpoint}` }, 404); + return createApiResponse(`Unsupported endpoint: ${endpoint}`, 404); } } From 01beb243a48cd2d4400a0458d4a0ba229678495b Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 23:02:36 -0700 Subject: [PATCH 045/128] refactor: Update metadata-generator endpoints to use createApiResponse consistently --- .../metadata-generator.do.ts | 57 ++++++++----------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/src/metadata-generator/metadata-generator.do.ts b/src/metadata-generator/metadata-generator.do.ts index 3e24d18..0eb0646 100644 --- a/src/metadata-generator/metadata-generator.do.ts +++ b/src/metadata-generator/metadata-generator.do.ts @@ -1,6 +1,6 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; -import { createJsonResponse } from '../utils/requests-responses'; +import { createApiResponse } from '../utils/requests-responses'; import { validateSharedKeyAuth } from '../utils/auth-helper'; interface TokenMetadata { @@ -48,19 +48,19 @@ export class MetadataGeneratorDO extends DurableObject { // Require auth for all endpoints const authResult = await validateSharedKeyAuth(this.env, request); if (!authResult.success) { - return createJsonResponse({ error: authResult.error }, authResult.status); + return createApiResponse(authResult.error, authResult.status); } // Extract contract ID from the path const contractIdMatch = endpoint.match(/\/(SP[A-Z0-9]+\.[^\/]+)/); if (!contractIdMatch) { - return createJsonResponse({ error: 'Invalid contract ID format' }, 400); + return createApiResponse('Invalid contract ID format', 400); } const contractId = contractIdMatch[1]; if (endpoint.startsWith('/generate/')) { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } return this.generateMetadata(contractId, request); } @@ -76,7 +76,7 @@ export class MetadataGeneratorDO extends DurableObject { return this.getMetadata(contractId); } - return createJsonResponse({ error: `Unsupported endpoint: ${endpoint}` }, 404); + return createApiResponse(`Unsupported endpoint: ${endpoint}`, 404); } private async generateMetadata(contractId: string, request: Request): Promise { @@ -87,12 +87,7 @@ export class MetadataGeneratorDO extends DurableObject { const requiredFields = ['name', 'symbol', 'decimals', 'description']; const missingFields = requiredFields.filter((field) => !(field in data)); if (missingFields.length > 0) { - return createJsonResponse( - { - error: `Missing required fields: ${missingFields.join(', ')}`, - }, - 400 - ); + return createApiResponse(`Missing required fields: ${missingFields.join(', ')}`, 400); } // Generate image using ImageGeneratorDO @@ -148,14 +143,13 @@ export class MetadataGeneratorDO extends DurableObject { }, }); - return createJsonResponse({ - success: true, - contractId, - metadata, + return createApiResponse({ + message: 'Successfully generated metadata', + data: { contractId, metadata } }); } catch (error) { console.error('Failed to generate metadata:', error); - return createJsonResponse({ error: 'Failed to generate metadata' }, 500); + return createApiResponse('Failed to generate metadata', 500); } } @@ -165,18 +159,21 @@ export class MetadataGeneratorDO extends DurableObject { const object = await this.env.AIBTCDEV_SERVICES_BUCKET.get(key); if (!object) { - return createJsonResponse({ error: 'Metadata not found' }, 404); + return createApiResponse('Metadata not found', 404); } const metadata: any = await object.json(); - return createJsonResponse({ - ...metadata, - contractId, - lastUpdated: object.uploaded, + return createApiResponse({ + message: 'Successfully retrieved metadata', + data: { + metadata, + contractId, + lastUpdated: object.uploaded + } }); } catch (error) { console.error('Failed to get metadata:', error); - return createJsonResponse({ error: 'Failed to retrieve metadata' }, 500); + return createApiResponse('Failed to retrieve metadata', 500); } } @@ -190,12 +187,7 @@ export class MetadataGeneratorDO extends DurableObject { const requiredFields = ['name', 'description', 'image']; const missingFields = requiredFields.filter((field) => !(field in updates)); if (missingFields.length > 0) { - return createJsonResponse( - { - error: `Missing required fields: ${missingFields.join(', ')}`, - }, - 400 - ); + return createApiResponse(`Missing required fields: ${missingFields.join(', ')}`, 400); } } @@ -222,14 +214,13 @@ export class MetadataGeneratorDO extends DurableObject { }, }); - return createJsonResponse({ - success: true, - contractId, - metadata: newMetadata, + return createApiResponse({ + message: 'Successfully updated metadata', + data: { contractId, metadata: newMetadata } }); } catch (error) { console.error('Failed to update metadata:', error); - return createJsonResponse({ error: 'Failed to update metadata' }, 500); + return createApiResponse('Failed to update metadata', 500); } } } From c7c9ed92d1c7974ea6f78001a037f95b32842f93 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 23:03:47 -0700 Subject: [PATCH 046/128] refactor: Update scheduler-do.ts to use createApiResponse consistently --- src/scheduler/scheduler-do.ts | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/scheduler/scheduler-do.ts b/src/scheduler/scheduler-do.ts index bfede4c..dbfe75e 100644 --- a/src/scheduler/scheduler-do.ts +++ b/src/scheduler/scheduler-do.ts @@ -1,7 +1,7 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; import { AppConfig } from '../config'; -import { createJsonResponse } from '../utils/requests-responses'; +import { createApiResponse } from '../utils/requests-responses'; /** * Durable Object class for scheduling and executing backend jobs @@ -43,12 +43,7 @@ export class SchedulerDO extends DurableObject { const path = url.pathname; if (!path.startsWith(this.BASE_PATH)) { - return createJsonResponse( - { - error: `Request at ${path} does not start with base path ${this.BASE_PATH}`, - }, - 404 - ); + return createApiResponse(`Request at ${path} does not start with base path ${this.BASE_PATH}`, 404); } // Remove base path to get the endpoint @@ -56,22 +51,18 @@ export class SchedulerDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { - return createJsonResponse({ - message: `Supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, + return createApiResponse({ + message: 'Available endpoints retrieved successfully', + data: { endpoints: this.SUPPORTED_ENDPOINTS } }); } if (endpoint === '/hello') { - return createJsonResponse({ - message: 'hello from scheduler!', + return createApiResponse({ + message: 'Hello from scheduler!', }); } - return createJsonResponse( - { - error: `Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, - }, - 404 - ); + return createApiResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); } } From 3a4e8300d8c230c6f323beb5328a432628a4d914 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Thu, 19 Dec 2024 23:05:08 -0700 Subject: [PATCH 047/128] refactor: Update tools-do.ts to use createApiResponse consistently --- src/tools/tools-do.ts | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/tools/tools-do.ts b/src/tools/tools-do.ts index f59b9a4..60bcb62 100644 --- a/src/tools/tools-do.ts +++ b/src/tools/tools-do.ts @@ -1,7 +1,7 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; import { AppConfig } from '../config'; -import { createJsonResponse } from '../utils/requests-responses'; +import { createApiResponse } from '../utils/requests-responses'; /** * Durable Object class for running Typescript tools requested by the backend @@ -43,12 +43,7 @@ export class ToolsDO extends DurableObject { const path = url.pathname; if (!path.startsWith(this.BASE_PATH)) { - return createJsonResponse( - { - error: `Request at ${path} does not start with base path ${this.BASE_PATH}`, - }, - 404 - ); + return createApiResponse(`Request at ${path} does not start with base path ${this.BASE_PATH}`, 404); } // Remove base path to get the endpoint @@ -56,22 +51,18 @@ export class ToolsDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { - return createJsonResponse({ - message: `Supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, + return createApiResponse({ + message: 'Available endpoints retrieved successfully', + data: { endpoints: this.SUPPORTED_ENDPOINTS } }); } if (endpoint === '/hello') { - return createJsonResponse({ - message: 'hello from tools!', + return createApiResponse({ + message: 'Hello from tools!' }); } - return createJsonResponse( - { - error: `Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, - }, - 404 - ); + return createApiResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); } } From 34a30887ac235ef64d3593f3e200de895215e982 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Thu, 19 Dec 2024 23:19:35 -0700 Subject: [PATCH 048/128] fix: cleanup missed items and lint --- src/auth/auth-do.ts | 17 ++- src/cdn/cdn-do.ts | 12 +- src/context/context-do.ts | 24 +--- src/database/database-do.ts | 117 +++++++++--------- .../metadata-generator.do.ts | 14 +-- src/utils/auth-helper.ts | 5 +- 6 files changed, 88 insertions(+), 101 deletions(-) diff --git a/src/auth/auth-do.ts b/src/auth/auth-do.ts index beb9741..6d4dd64 100644 --- a/src/auth/auth-do.ts +++ b/src/auth/auth-do.ts @@ -97,10 +97,7 @@ export class AuthDO extends DurableObject { } // check if address is valid if (!isAddressValid) { - return createApiResponse( - `Invalid address ${addressFromPubkey} from public key ${publicKey}`, - 400 - ); + return createApiResponse(`Invalid address ${addressFromPubkey} from public key ${publicKey}`, 400); } // add address to kv key list with unique session key // expires after 30 days and requires new signature from user @@ -125,8 +122,8 @@ export class AuthDO extends DurableObject { message: 'Successfully created auth token', data: { address: addressFromPubkey, - sessionToken - } + sessionToken, + }, }); } catch (error) { return createApiResponse(`Failed to verify signature: ${error instanceof Error ? error.message : String(error)}`, 401); @@ -154,8 +151,8 @@ export class AuthDO extends DurableObject { message: 'Successfully verified address', data: { address, - sessionKey - } + sessionKey, + }, }); } @@ -176,8 +173,8 @@ export class AuthDO extends DurableObject { message: 'Successfully verified session token', data: { address, - sessionToken - } + sessionToken, + }, }); } diff --git a/src/cdn/cdn-do.ts b/src/cdn/cdn-do.ts index 2f6cfd7..8d2f4ee 100644 --- a/src/cdn/cdn-do.ts +++ b/src/cdn/cdn-do.ts @@ -53,8 +53,8 @@ export class CdnDO extends DurableObject { return createApiResponse({ message: 'Welcome to CDN service', data: { - supportedEndpoints: this.SUPPORTED_ENDPOINTS - } + supportedEndpoints: this.SUPPORTED_ENDPOINTS, + }, }); } @@ -109,8 +109,8 @@ export class CdnDO extends DurableObject { httpEtag: obj.httpEtag, })), truncated: objects.truncated, - cursor: objects.truncated ? objects.cursor : undefined - } + cursor: objects.truncated ? objects.cursor : undefined, + }, }); } catch (error) { return createApiResponse('Failed to list objects', 500); @@ -132,7 +132,7 @@ export class CdnDO extends DurableObject { return createApiResponse({ message: 'Successfully stored object', - data: { r2ObjectKey, etag: object.httpEtag } + data: { r2ObjectKey, etag: object.httpEtag }, }); } catch (error) { return createApiResponse('Failed to store object', 500); @@ -144,7 +144,7 @@ export class CdnDO extends DurableObject { await this.env.AIBTCDEV_SERVICES_BUCKET.delete(r2ObjectKey); return createApiResponse({ message: 'Successfully deleted object', - data: { r2ObjectKey } + data: { r2ObjectKey }, }); } catch (error) { return createApiResponse('Failed to delete object', 500); diff --git a/src/context/context-do.ts b/src/context/context-do.ts index df03e7e..5e3f4fd 100644 --- a/src/context/context-do.ts +++ b/src/context/context-do.ts @@ -1,7 +1,7 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; import { AppConfig } from '../config'; -import { createJsonResponse } from '../utils/requests-responses'; +import { createApiResponse } from '../utils/requests-responses'; /** * Durable Object class for providing transformed data as API endpoints @@ -43,12 +43,7 @@ export class ContextDO extends DurableObject { const path = url.pathname; if (!path.startsWith(this.BASE_PATH)) { - return createJsonResponse( - { - error: `Request at ${path} does not start with base path ${this.BASE_PATH}`, - }, - 404 - ); + return createApiResponse(`Request at ${path} does not start with base path ${this.BASE_PATH}`, 404); } // Remove base path to get the endpoint @@ -56,22 +51,13 @@ export class ContextDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { - return createJsonResponse({ - message: `Supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, - }); + return createApiResponse(`Supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`); } if (endpoint === '/hello') { - return createJsonResponse({ - message: 'hello from context!', - }); + return createApiResponse('hello from context!'); } - return createJsonResponse( - { - error: `Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, - }, - 404 - ); + return createApiResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); } } diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 0ccfdc9..58e5205 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -148,8 +148,8 @@ export class DatabaseDO extends DurableObject { return createApiResponse({ message: 'Database service endpoints available', data: { - endpoints: this.SUPPORTED_ENDPOINTS - } + endpoints: this.SUPPORTED_ENDPOINTS, + }, }); } @@ -170,7 +170,7 @@ export class DatabaseDO extends DurableObject { const conversations = await getConversations(this.orm, address); return createApiResponse({ message: 'Successfully retrieved conversations', - data: conversations + data: conversations, }); } @@ -182,7 +182,7 @@ export class DatabaseDO extends DurableObject { const conversation = await getLatestConversation(this.orm, address); return createApiResponse({ message: 'Successfully retrieved latest conversation', - data: conversation + data: conversation, }); } @@ -194,7 +194,7 @@ export class DatabaseDO extends DurableObject { const history = await getConversationHistory(this.orm, parseInt(conversationId)); return createApiResponse({ message: 'Successfully retrieved conversation history', - data: history + data: history, }); } @@ -223,7 +223,7 @@ export class DatabaseDO extends DurableObject { const crews = await getCrewsByProfile(this.orm, address); return createApiResponse({ message: 'Successfully retrieved profile crews', - data: { crews } + data: { crews }, }); } @@ -231,7 +231,7 @@ export class DatabaseDO extends DurableObject { const crews = await getPublicCrews(this.orm); return createApiResponse({ message: 'Successfully retrieved public crews', - data: { crews } + data: { crews }, }); } @@ -243,7 +243,7 @@ export class DatabaseDO extends DurableObject { const crew = await getCrew(this.orm, parseInt(crewId)); return createApiResponse({ message: 'Successfully retrieved crew', - data: { crew } + data: { crew }, }); } @@ -258,7 +258,7 @@ export class DatabaseDO extends DurableObject { const crew = await createCrew(this.orm, crewData); return createApiResponse({ message: 'Successfully created crew', - data: { crew } + data: { crew }, }); } @@ -274,7 +274,7 @@ export class DatabaseDO extends DurableObject { const result = await updateCrew(this.orm, parseInt(crewId), updates); return createApiResponse({ message: 'Successfully updated crew', - data: { result } + data: { result }, }); } @@ -289,7 +289,7 @@ export class DatabaseDO extends DurableObject { const result = await deleteCrew(this.orm, parseInt(crewId)); return createApiResponse({ message: 'Successfully deleted crew', - data: { result } + data: { result }, }); } @@ -301,8 +301,8 @@ export class DatabaseDO extends DurableObject { } const agents = await getAgents(this.orm, parseInt(crewId)); return createApiResponse({ - message: 'Successfully retrieved agents', - data: { agents } + message: 'Successfully retrieved agents', + data: { agents }, }); } @@ -319,12 +319,15 @@ export class DatabaseDO extends DurableObject { !agentData.agent_goal || !agentData.agent_backstory ) { - return createApiResponse('Missing required fields: profile_id, crew_id, agent_name, agent_role, agent_goal, agent_backstory', 400); + return createApiResponse( + 'Missing required fields: profile_id, crew_id, agent_name, agent_role, agent_goal, agent_backstory', + 400 + ); } const agent = await createAgent(this.orm, agentData); return createApiResponse({ message: 'Successfully created agent', - data: { agent } + data: { agent }, }); } @@ -342,7 +345,7 @@ export class DatabaseDO extends DurableObject { const result = await updateAgent(this.orm, parseInt(agentId), updates); return createApiResponse({ message: 'Successfully updated agent', - data: { result } + data: { result }, }); } @@ -357,7 +360,7 @@ export class DatabaseDO extends DurableObject { const result = await deleteAgent(this.orm, parseInt(agentId)); return createApiResponse({ message: 'Successfully deleted agent', - data: { result } + data: { result }, }); } @@ -370,7 +373,7 @@ export class DatabaseDO extends DurableObject { const task = await getTask(this.orm, parseInt(taskId)); return createApiResponse({ message: 'Successfully retrieved task', - data: { task } + data: { task }, }); } @@ -382,7 +385,7 @@ export class DatabaseDO extends DurableObject { const tasks = await getTasks(this.orm, parseInt(agentId)); return createApiResponse({ message: 'Successfully retrieved tasks', - data: { tasks } + data: { tasks }, }); } @@ -399,12 +402,15 @@ export class DatabaseDO extends DurableObject { !taskData.task_description || !taskData.task_expected_output ) { - return createApiResponse('Missing required fields: profile_id, crew_id, agent_id, task_name, task_description, task_expected_output', 400); + return createApiResponse( + 'Missing required fields: profile_id, crew_id, agent_id, task_name, task_description, task_expected_output', + 400 + ); } const task = await createTask(this.orm, taskData); return createApiResponse({ message: 'Successfully created task', - data: { task } + data: { task }, }); } @@ -422,7 +428,7 @@ export class DatabaseDO extends DurableObject { const result = await updateTask(this.orm, parseInt(taskId), updates); return createApiResponse({ message: 'Successfully updated task', - data: { result } + data: { result }, }); } @@ -437,7 +443,7 @@ export class DatabaseDO extends DurableObject { const result = await deleteTask(this.orm, parseInt(taskId)); return createApiResponse({ message: 'Successfully deleted task', - data: { result } + data: { result }, }); } @@ -452,19 +458,19 @@ export class DatabaseDO extends DurableObject { const result = await deleteTasks(this.orm, parseInt(agentId)); return createApiResponse({ message: 'Successfully deleted all tasks for agent', - data: { result } + data: { result }, }); } if (endpoint === '/crews/executions') { const address = url.searchParams.get('address'); if (!address) { - return createJsonResponse('Missing address parameter', 400); + return createApiResponse('Missing address parameter', 400); } const executions = await getCrewExecutions(this.orm, address); return createApiResponse({ message: 'Successfully retrieved crew executions', - data: { executions } + data: { executions }, }); } @@ -490,7 +496,7 @@ export class DatabaseDO extends DurableObject { const execution = await addCrewExecution(this.orm, address, crewId, conversationId, input); return createApiResponse({ message: 'Successfully created crew execution', - data: { execution } + data: { execution }, }); } @@ -499,7 +505,7 @@ export class DatabaseDO extends DurableObject { const crons = await getEnabledCrons(this.orm); return createApiResponse({ message: 'Successfully retrieved enabled crons', - data: { crons } + data: { crons }, }); } @@ -507,7 +513,7 @@ export class DatabaseDO extends DurableObject { const crons = await getEnabledCronsDetailed(this.orm); return createApiResponse({ message: 'Successfully retrieved detailed cron information', - data: { crons } + data: { crons }, }); } @@ -520,7 +526,7 @@ export class DatabaseDO extends DurableObject { const crons = await getCronsByCrew(this.orm, parseInt(crewId)); return createApiResponse({ message: 'Successfully retrieved crons for crew', - data: { crons } + data: { crons }, }); } @@ -538,7 +544,7 @@ export class DatabaseDO extends DurableObject { const cron = await createCron(this.orm, cronData); return createApiResponse({ message: 'Successfully created cron', - data: { cron } + data: { cron }, }); } @@ -557,7 +563,7 @@ export class DatabaseDO extends DurableObject { const result = await updateCronInput(this.orm, parseInt(cronId), cron_input); return createApiResponse({ message: 'Successfully updated user profile', - data: { result } + data: { result }, }); } @@ -576,14 +582,14 @@ export class DatabaseDO extends DurableObject { const result = await toggleCronStatus(this.orm, parseInt(cronId), cron_enabled ? 1 : 0); return createApiResponse({ message: 'Successfully toggled cron status', - data: { result } + data: { result }, }); } // Conversation creation endpoint if (endpoint === '/conversations/create') { if (request.method !== 'POST') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } const { profile_id, conversation_name } = (await request.json()) as UserConversationsTable; @@ -594,7 +600,7 @@ export class DatabaseDO extends DurableObject { const result = await addConversation(this.orm, profile_id, conversation_name ? conversation_name : 'new conversation'); return createApiResponse({ message: 'Successfully updated user profile', - data: { result } + data: { result }, }); } @@ -607,7 +613,7 @@ export class DatabaseDO extends DurableObject { const role = await getUserRole(this.orm, address); return createApiResponse({ message: 'Successfully retrieved user role', - data: { role } + data: { role }, }); } @@ -619,7 +625,7 @@ export class DatabaseDO extends DurableObject { const profile = await getUserProfile(this.orm, address); return createApiResponse({ message: 'Successfully retrieved user profile', - data: { profile } + data: { profile }, }); } @@ -634,7 +640,7 @@ export class DatabaseDO extends DurableObject { const profile = await createUserProfile(this.orm, profileData); return createApiResponse({ message: 'Successfully created user profile', - data: { profile } + data: { profile }, }); } @@ -650,7 +656,7 @@ export class DatabaseDO extends DurableObject { const result = await updateUserProfile(this.orm, address, profileData); return createApiResponse({ message: 'Successfully updated user profile', - data: { result } + data: { result }, }); } @@ -665,7 +671,7 @@ export class DatabaseDO extends DurableObject { const result = await deleteUserProfile(this.orm, address); return createApiResponse({ message: 'Successfully deleted user profile', - data: { result } + data: { result }, }); } @@ -674,7 +680,7 @@ export class DatabaseDO extends DurableObject { const profiles = await getAllUserProfiles(this.orm); return createApiResponse({ message: 'Successfully retrieved all user profiles', - data: { profiles } + data: { profiles }, }); } @@ -690,7 +696,7 @@ export class DatabaseDO extends DurableObject { const result = await updateUserProfileById(this.orm, parseInt(userId), updates); return createApiResponse({ message: 'Successfully updated user profile', - data: { result } + data: { result }, }); } } catch (error) { @@ -707,7 +713,7 @@ export class DatabaseDO extends DurableObject { const author = await getAuthor(this.orm, authorId); return createApiResponse({ message: 'Successfully retrieved author', - data: { author } + data: { author }, }); } @@ -722,7 +728,7 @@ export class DatabaseDO extends DurableObject { const author = await addAuthor(this.orm, author_id, realname || undefined, username || undefined); return createApiResponse({ message: 'Successfully created author', - data: { author } + data: { author }, }); } @@ -734,7 +740,7 @@ export class DatabaseDO extends DurableObject { const tweet = await getTweet(this.orm, tweetId); return createApiResponse({ message: 'Successfully retrieved tweet', - data: { tweet } + data: { tweet }, }); } @@ -746,7 +752,7 @@ export class DatabaseDO extends DurableObject { const tweets = await getThreadTweets(this.orm, parseInt(threadId)); return createApiResponse({ message: 'Successfully retrieved thread tweets', - data: { tweets } + data: { tweets }, }); } @@ -758,7 +764,7 @@ export class DatabaseDO extends DurableObject { const tweets = await getAuthorTweets(this.orm, authorId); return createApiResponse({ message: 'Successfully retrieved author tweets', - data: { tweets } + data: { tweets }, }); } @@ -781,7 +787,7 @@ export class DatabaseDO extends DurableObject { ); return createApiResponse({ message: 'Successfully created tweet', - data: { tweet } + data: { tweet }, }); } @@ -793,7 +799,7 @@ export class DatabaseDO extends DurableObject { const logs = await getTweetLogs(this.orm, tweetId); return createApiResponse({ message: 'Successfully retrieved tweet logs', - data: { logs } + data: { logs }, }); } @@ -808,7 +814,7 @@ export class DatabaseDO extends DurableObject { const log = await addLog(this.orm, tweet_id, tweet_status, log_message || undefined); return createApiResponse({ message: 'Successfully created log', - data: { log } + data: { log }, }); } @@ -837,7 +843,7 @@ export class DatabaseDO extends DurableObject { const steps = await getExecutionSteps(this.orm, parseInt(executionId)); return createApiResponse({ message: 'Successfully retrieved execution steps', - data: { steps } + data: { steps }, }); } @@ -874,7 +880,7 @@ export class DatabaseDO extends DurableObject { const step = await createExecutionStep(this.orm, stepData); return createApiResponse({ message: 'Successfully created execution step', - data: { step } + data: { step }, }); } @@ -906,13 +912,10 @@ export class DatabaseDO extends DurableObject { const result = await deleteExecutionSteps(this.orm, parseInt(executionId)); return createApiResponse({ message: 'Successfully deleted execution steps', - data: { result } + data: { result }, }); } - return createApiResponse( - `Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, - 404 - ); + return createApiResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); } } diff --git a/src/metadata-generator/metadata-generator.do.ts b/src/metadata-generator/metadata-generator.do.ts index 0eb0646..b750e16 100644 --- a/src/metadata-generator/metadata-generator.do.ts +++ b/src/metadata-generator/metadata-generator.do.ts @@ -67,7 +67,7 @@ export class MetadataGeneratorDO extends DurableObject { if (endpoint.startsWith('/update/')) { if (request.method !== 'POST' && request.method !== 'PATCH') { - return createJsonResponse({ error: 'Method not allowed' }, 405); + return createApiResponse('Method not allowed', 405); } return this.updateMetadata(contractId, request); } @@ -91,8 +91,8 @@ export class MetadataGeneratorDO extends DurableObject { } // Generate image using ImageGeneratorDO - const imageGeneratorId = this.env.IMAGES_DO.idFromName(this.IMAGE_DO_NAME); - const imageGenerator = this.env.IMAGES_DO.get(imageGeneratorId); + const imageGeneratorId = this.env.IMAGE_GENERATOR_DO.idFromName(this.IMAGE_DO_NAME); + const imageGenerator = this.env.IMAGE_GENERATOR_DO.get(imageGeneratorId); // Create image prompt based on token details const imagePrompt = @@ -145,7 +145,7 @@ export class MetadataGeneratorDO extends DurableObject { return createApiResponse({ message: 'Successfully generated metadata', - data: { contractId, metadata } + data: { contractId, metadata }, }); } catch (error) { console.error('Failed to generate metadata:', error); @@ -168,8 +168,8 @@ export class MetadataGeneratorDO extends DurableObject { data: { metadata, contractId, - lastUpdated: object.uploaded - } + lastUpdated: object.uploaded, + }, }); } catch (error) { console.error('Failed to get metadata:', error); @@ -216,7 +216,7 @@ export class MetadataGeneratorDO extends DurableObject { return createApiResponse({ message: 'Successfully updated metadata', - data: { contractId, metadata: newMetadata } + data: { contractId, metadata: newMetadata }, }); } catch (error) { console.error('Failed to update metadata:', error); diff --git a/src/utils/auth-helper.ts b/src/utils/auth-helper.ts index 642fa05..c7553aa 100644 --- a/src/utils/auth-helper.ts +++ b/src/utils/auth-helper.ts @@ -1,8 +1,9 @@ import { Env } from '../../worker-configuration'; -type SharedKeyResponse = { success: boolean; error?: string; status?: number }; +type SharedKeySuccess = { success: true }; +type SharedKeyFailure = { success: false; error: string; status: number }; -export async function validateSharedKeyAuth(env: Env, request: Request): Promise { +export async function validateSharedKeyAuth(env: Env, request: Request): Promise { if (!request.headers.has('Authorization')) { return { success: false, error: 'Missing Authorization header', status: 401 }; } From e6fab57caf0597dcee14fed17a32ec3de5e99026 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Thu, 19 Dec 2024 23:28:54 -0700 Subject: [PATCH 049/128] refactor: Reorder supported CDN endpoints alphabetically --- src/cdn/cdn-do.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdn/cdn-do.ts b/src/cdn/cdn-do.ts index 8d2f4ee..6f12053 100644 --- a/src/cdn/cdn-do.ts +++ b/src/cdn/cdn-do.ts @@ -8,7 +8,7 @@ export class CdnDO extends DurableObject { private readonly ALARM_INTERVAL_MS: number; private readonly BASE_PATH = '/cdn'; private readonly KEY_PREFIX = 'cdn'; - private readonly SUPPORTED_ENDPOINTS: string[] = ['/get', '/put', '/delete', '/list']; + private readonly SUPPORTED_ENDPOINTS: string[] = ['/get', '/list', '/put', '/delete']; constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); From 7723d5a6d411cca8e1554c2b198d7ff696f2be39 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 00:18:06 -0700 Subject: [PATCH 050/128] fix: standardize root and unrecognized route patterns also introduces a new endpoint to dry up code a bit --- src/auth/auth-do.ts | 9 ++++++--- src/cdn/cdn-do.ts | 8 ++++---- src/context/context-do.ts | 12 ++++-------- src/database/database-do.ts | 6 +++--- src/image-generator/image-generator.do.ts | 18 ++++++++++++++---- src/index.ts | 14 +++++++++----- .../metadata-generator.do.ts | 18 ++++++++++++++---- src/scheduler/scheduler-do.ts | 17 ++++------------- src/tools/tools-do.ts | 17 ++++------------- src/utils/requests-responses.ts | 4 ++++ 10 files changed, 66 insertions(+), 57 deletions(-) diff --git a/src/auth/auth-do.ts b/src/auth/auth-do.ts index 6d4dd64..525d620 100644 --- a/src/auth/auth-do.ts +++ b/src/auth/auth-do.ts @@ -3,7 +3,7 @@ import { verifyMessageSignatureRsv } from '@stacks/encryption'; import { getAddressFromPublicKey, validateStacksAddress } from '@stacks/transactions'; import { Env } from '../../worker-configuration'; import { AppConfig } from '../config'; -import { createApiResponse } from '../utils/requests-responses'; +import { createApiResponse, createUnsupportedEndpointResponse } from '../utils/requests-responses'; import { validateSharedKeyAuth } from '../utils/auth-helper'; /** @@ -57,7 +57,10 @@ export class AuthDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { return createApiResponse({ - message: `Supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, + message: 'auth service', + data: { + endpoints: this.SUPPORTED_ENDPOINTS, + }, }); } @@ -180,6 +183,6 @@ export class AuthDO extends DurableObject { // TODO: endpoint to revoke a session token - return createApiResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); + return createUnsupportedEndpointResponse(endpoint, this.SUPPORTED_ENDPOINTS); } } diff --git a/src/cdn/cdn-do.ts b/src/cdn/cdn-do.ts index 6f12053..55c9b89 100644 --- a/src/cdn/cdn-do.ts +++ b/src/cdn/cdn-do.ts @@ -1,6 +1,6 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; -import { createApiResponse } from '../utils/requests-responses'; +import { createApiResponse, createUnsupportedEndpointResponse } from '../utils/requests-responses'; import { validateSharedKeyAuth } from '../utils/auth-helper'; import { AppConfig } from '../config'; @@ -51,9 +51,9 @@ export class CdnDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { return createApiResponse({ - message: 'Welcome to CDN service', + message: 'cdn service', data: { - supportedEndpoints: this.SUPPORTED_ENDPOINTS, + endpoints: this.SUPPORTED_ENDPOINTS, }, }); } @@ -151,6 +151,6 @@ export class CdnDO extends DurableObject { } } - return createApiResponse(`Unsupported endpoint: ${endpoint}`, 404); + return createUnsupportedEndpointResponse(endpoint, this.SUPPORTED_ENDPOINTS); } } diff --git a/src/context/context-do.ts b/src/context/context-do.ts index 5e3f4fd..2687661 100644 --- a/src/context/context-do.ts +++ b/src/context/context-do.ts @@ -1,7 +1,7 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; import { AppConfig } from '../config'; -import { createApiResponse } from '../utils/requests-responses'; +import { createApiResponse, createUnsupportedEndpointResponse } from '../utils/requests-responses'; /** * Durable Object class for providing transformed data as API endpoints @@ -9,7 +9,7 @@ import { createApiResponse } from '../utils/requests-responses'; export class ContextDO extends DurableObject { private readonly ALARM_INTERVAL_MS: number; private readonly BASE_PATH: string = '/context'; - private readonly SUPPORTED_ENDPOINTS: string[] = ['/hello']; + private readonly SUPPORTED_ENDPOINTS: string[] = ['not implemented yet']; constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); @@ -51,13 +51,9 @@ export class ContextDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { - return createApiResponse(`Supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`); + return createApiResponse('Not implemented yet', 405); } - if (endpoint === '/hello') { - return createApiResponse('hello from context!'); - } - - return createApiResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); + return createUnsupportedEndpointResponse(endpoint, this.SUPPORTED_ENDPOINTS); } } diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 58e5205..6a71881 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -1,7 +1,7 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; import { AppConfig } from '../config'; -import { createApiResponse } from '../utils/requests-responses'; +import { createApiResponse, createUnsupportedEndpointResponse } from '../utils/requests-responses'; import { D1Orm } from 'd1-orm'; import { UserAgentsTable, @@ -146,7 +146,7 @@ export class DatabaseDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { return createApiResponse({ - message: 'Database service endpoints available', + message: 'database service', data: { endpoints: this.SUPPORTED_ENDPOINTS, }, @@ -916,6 +916,6 @@ export class DatabaseDO extends DurableObject { }); } - return createApiResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); + return createUnsupportedEndpointResponse(endpoint, this.SUPPORTED_ENDPOINTS); } } diff --git a/src/image-generator/image-generator.do.ts b/src/image-generator/image-generator.do.ts index 101a171..604e703 100644 --- a/src/image-generator/image-generator.do.ts +++ b/src/image-generator/image-generator.do.ts @@ -1,6 +1,6 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; -import { createJsonResponse } from '../utils/requests-responses'; +import { createApiResponse, createUnsupportedEndpointResponse } from '../utils/requests-responses'; import { validateSharedKeyAuth } from '../utils/auth-helper'; import OpenAI from 'openai'; @@ -31,6 +31,16 @@ export class ImageGeneratorDO extends DurableObject { const endpoint = path.replace(this.BASE_PATH, ''); + // Handle root path + if (endpoint === '' || endpoint === '/') { + return createApiResponse({ + message: 'image generator service', + data: { + endpoints: this.SUPPORTED_ENDPOINTS, + }, + }); + } + // Require auth for all endpoints const authResult = await validateSharedKeyAuth(this.env, request); if (!authResult.success) { @@ -91,7 +101,7 @@ export class ImageGeneratorDO extends DurableObject { return createApiResponse({ message: 'Successfully generated images', - data: { images: storedImages } + data: { images: storedImages }, }); } catch (error) { console.error('Image generation failed:', error); @@ -141,13 +151,13 @@ export class ImageGeneratorDO extends DurableObject { })), truncated: objects.truncated, cursor: objects.truncated ? objects.cursor : undefined, - } + }, }); } catch (error) { return createApiResponse('Failed to list images', 500); } } - return createApiResponse(`Unsupported endpoint: ${endpoint}`, 404); + return createUnsupportedEndpointResponse(endpoint, this.SUPPORTED_ENDPOINTS); } } diff --git a/src/index.ts b/src/index.ts index 9165b01..e53d609 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import { Env } from '../worker-configuration'; import { AppConfig } from './config'; -import { corsHeaders, createJsonResponse } from './utils/requests-responses'; +import { corsHeaders, createApiResponse, createUnsupportedEndpointResponse } from './utils/requests-responses'; import { AuthDO } from './auth/auth-do'; import { CdnDO } from './cdn/cdn-do'; import { ContextDO } from './context/context-do'; @@ -35,9 +35,13 @@ export default { const url = new URL(request.url); const path = url.pathname; - if (path === '/') { - return createJsonResponse({ - message: `Welcome to aibtcdev-services! Supported services: ${config.SUPPORTED_SERVICES.join(', ')}`, + // Handle root path + if (path === '' || path === '/') { + return createApiResponse({ + message: 'aibtc.dev services online', + data: { + endpoints: config.SUPPORTED_SERVICES, + }, }); } @@ -92,6 +96,6 @@ export default { } // Return 404 for any other path - return createJsonResponse(`Unsupported service at: ${path}, supported services: ${config.SUPPORTED_SERVICES.join(', ')}`, 404); + return createUnsupportedEndpointResponse(path, config.SUPPORTED_SERVICES); }, } satisfies ExportedHandler; diff --git a/src/metadata-generator/metadata-generator.do.ts b/src/metadata-generator/metadata-generator.do.ts index b750e16..5edfb77 100644 --- a/src/metadata-generator/metadata-generator.do.ts +++ b/src/metadata-generator/metadata-generator.do.ts @@ -1,6 +1,6 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; -import { createApiResponse } from '../utils/requests-responses'; +import { createApiResponse, createUnsupportedEndpointResponse } from '../utils/requests-responses'; import { validateSharedKeyAuth } from '../utils/auth-helper'; interface TokenMetadata { @@ -31,7 +31,7 @@ interface GenerateMetadataRequest { export class MetadataGeneratorDO extends DurableObject { private readonly BASE_PATH = '/metadata'; private readonly KEY_PREFIX = 'sip10'; - private readonly IMAGE_DO_NAME = 'IMAGE_GENERATOR'; + private readonly SUPPORTED_ENDPOINTS = ['/generate', '/update']; constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); @@ -45,6 +45,16 @@ export class MetadataGeneratorDO extends DurableObject { const endpoint = path.replace(this.BASE_PATH, ''); + // Handle root path + if (endpoint === '' || endpoint === '/') { + return createApiResponse({ + message: 'metadata generator service', + data: { + endpoints: this.SUPPORTED_ENDPOINTS, + }, + }); + } + // Require auth for all endpoints const authResult = await validateSharedKeyAuth(this.env, request); if (!authResult.success) { @@ -76,7 +86,7 @@ export class MetadataGeneratorDO extends DurableObject { return this.getMetadata(contractId); } - return createApiResponse(`Unsupported endpoint: ${endpoint}`, 404); + return createUnsupportedEndpointResponse(endpoint, this.SUPPORTED_ENDPOINTS); } private async generateMetadata(contractId: string, request: Request): Promise { @@ -91,7 +101,7 @@ export class MetadataGeneratorDO extends DurableObject { } // Generate image using ImageGeneratorDO - const imageGeneratorId = this.env.IMAGE_GENERATOR_DO.idFromName(this.IMAGE_DO_NAME); + const imageGeneratorId = this.env.IMAGE_GENERATOR_DO.idFromName('image-generator-do'); const imageGenerator = this.env.IMAGE_GENERATOR_DO.get(imageGeneratorId); // Create image prompt based on token details diff --git a/src/scheduler/scheduler-do.ts b/src/scheduler/scheduler-do.ts index dbfe75e..38d1771 100644 --- a/src/scheduler/scheduler-do.ts +++ b/src/scheduler/scheduler-do.ts @@ -1,7 +1,7 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; import { AppConfig } from '../config'; -import { createApiResponse } from '../utils/requests-responses'; +import { createApiResponse, createUnsupportedEndpointResponse } from '../utils/requests-responses'; /** * Durable Object class for scheduling and executing backend jobs @@ -9,7 +9,7 @@ import { createApiResponse } from '../utils/requests-responses'; export class SchedulerDO extends DurableObject { private readonly ALARM_INTERVAL_MS: number; private readonly BASE_PATH: string = '/scheduler'; - private readonly SUPPORTED_ENDPOINTS: string[] = ['/hello']; + private readonly SUPPORTED_ENDPOINTS: string[] = ['not implemented yet']; constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); @@ -51,18 +51,9 @@ export class SchedulerDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { - return createApiResponse({ - message: 'Available endpoints retrieved successfully', - data: { endpoints: this.SUPPORTED_ENDPOINTS } - }); + return createApiResponse('Not implemented yet', 405); } - if (endpoint === '/hello') { - return createApiResponse({ - message: 'Hello from scheduler!', - }); - } - - return createApiResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); + return createUnsupportedEndpointResponse(endpoint, this.SUPPORTED_ENDPOINTS); } } diff --git a/src/tools/tools-do.ts b/src/tools/tools-do.ts index 60bcb62..4912bf8 100644 --- a/src/tools/tools-do.ts +++ b/src/tools/tools-do.ts @@ -1,7 +1,7 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; import { AppConfig } from '../config'; -import { createApiResponse } from '../utils/requests-responses'; +import { createApiResponse, createUnsupportedEndpointResponse } from '../utils/requests-responses'; /** * Durable Object class for running Typescript tools requested by the backend @@ -9,7 +9,7 @@ import { createApiResponse } from '../utils/requests-responses'; export class ToolsDO extends DurableObject { private readonly ALARM_INTERVAL_MS: number; private readonly BASE_PATH: string = '/tools'; - private readonly SUPPORTED_ENDPOINTS: string[] = ['/hello']; + private readonly SUPPORTED_ENDPOINTS: string[] = ['not implemented yet']; constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); @@ -51,18 +51,9 @@ export class ToolsDO extends DurableObject { // Handle root path if (endpoint === '' || endpoint === '/') { - return createApiResponse({ - message: 'Available endpoints retrieved successfully', - data: { endpoints: this.SUPPORTED_ENDPOINTS } - }); + return createApiResponse('Not implemented yet', 405); } - if (endpoint === '/hello') { - return createApiResponse({ - message: 'Hello from tools!' - }); - } - - return createApiResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${this.SUPPORTED_ENDPOINTS.join(', ')}`, 404); + return createUnsupportedEndpointResponse(endpoint, this.SUPPORTED_ENDPOINTS); } } diff --git a/src/utils/requests-responses.ts b/src/utils/requests-responses.ts index 2807c48..37e8b1c 100644 --- a/src/utils/requests-responses.ts +++ b/src/utils/requests-responses.ts @@ -31,3 +31,7 @@ export function createApiResponse(response: { message: string; data?: unknown } }, }); } + +export function createUnsupportedEndpointResponse(endpoint: string, supportedEndpoints: string[]): Response { + return createApiResponse(`Unsupported endpoint: ${endpoint}, supported endpoints: ${supportedEndpoints.join(', ')}`, 404); +} From de75e95956d939aea81b46766caffc3a42d2933a Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 00:19:06 -0700 Subject: [PATCH 051/128] fix: Resolve type error in createApiResponse by safely spreading data --- src/utils/requests-responses.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils/requests-responses.ts b/src/utils/requests-responses.ts index 37e8b1c..cffad8a 100644 --- a/src/utils/requests-responses.ts +++ b/src/utils/requests-responses.ts @@ -13,13 +13,15 @@ export function corsHeaders(origin?: string): HeadersInit { }; } -export function createApiResponse(response: { message: string; data?: unknown } | string, status: number = 200): Response { +export function createApiResponse(response: { message: string; data?: Record } | string, status: number = 200): Response { const isOk = status >= 200 && status < 300; const responseBody: ApiResponse = { success: isOk, ...(isOk ? { - data: typeof response === 'string' ? { message: response } : { message: response.message, ...response.data }, + data: typeof response === 'string' + ? { message: response } + : { message: response.message, ...(response.data || {}) }, } : { error: response as string }), }; From a5c7918eb7cd7f96fbe7a055a178bfd6001bbacf Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 00:20:30 -0700 Subject: [PATCH 052/128] refactor: Simplify createApiResponse function with inline ternary logic --- src/utils/requests-responses.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/utils/requests-responses.ts b/src/utils/requests-responses.ts index cffad8a..6a157b4 100644 --- a/src/utils/requests-responses.ts +++ b/src/utils/requests-responses.ts @@ -19,9 +19,7 @@ export function createApiResponse(response: { message: string; data?: Record Date: Fri, 20 Dec 2024 00:20:38 -0700 Subject: [PATCH 053/128] refactor: Simplify createApiResponse function with improved readability --- src/utils/requests-responses.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/utils/requests-responses.ts b/src/utils/requests-responses.ts index 6a157b4..446b677 100644 --- a/src/utils/requests-responses.ts +++ b/src/utils/requests-responses.ts @@ -15,14 +15,17 @@ export function corsHeaders(origin?: string): HeadersInit { export function createApiResponse(response: { message: string; data?: Record } | string, status: number = 200): Response { const isOk = status >= 200 && status < 300; + const isStringResponse = typeof response === 'string'; + + const responseData = isStringResponse + ? { message: response } + : { message: response.message, ...(response.data || {}) }; + const responseBody: ApiResponse = { success: isOk, - ...(isOk - ? { - data: typeof response === 'string' ? { message: response } : { message: response.message, ...(response.data || {}) }, - } - : { error: response as string }), + ...(isOk ? { data: responseData } : { error: response as string }), }; + return new Response(JSON.stringify(responseBody), { status, headers: { From f7f4a147c1dc87fd11832f3b4ba93ddfd4d21133 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 00:24:30 -0700 Subject: [PATCH 054/128] refactor: Make API response data type more flexible with `unknown` --- src/utils/requests-responses.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/requests-responses.ts b/src/utils/requests-responses.ts index 446b677..2672561 100644 --- a/src/utils/requests-responses.ts +++ b/src/utils/requests-responses.ts @@ -13,7 +13,7 @@ export function corsHeaders(origin?: string): HeadersInit { }; } -export function createApiResponse(response: { message: string; data?: Record } | string, status: number = 200): Response { +export function createApiResponse(response: { message: string; data?: unknown } | string, status: number = 200): Response { const isOk = status >= 200 && status < 300; const isStringResponse = typeof response === 'string'; From dc2e247c05449c5a1f8c4a47497d9cff4779af12 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 00:44:51 -0700 Subject: [PATCH 055/128] docs: make a home for refs --- docs/endpoint_structure.md | 90 ++++++++++++++++++++++++++++++++++ src/consistent_endpoints.md | 97 ------------------------------------- 2 files changed, 90 insertions(+), 97 deletions(-) create mode 100644 docs/endpoint_structure.md delete mode 100644 src/consistent_endpoints.md diff --git a/docs/endpoint_structure.md b/docs/endpoint_structure.md new file mode 100644 index 0000000..1b96457 --- /dev/null +++ b/docs/endpoint_structure.md @@ -0,0 +1,90 @@ +## 1. Successful Responses (Status Codes 200-299) + +### When Returning Data + +```javascript +createApiResponse({ + message: 'A clear description of what succeeded', + data: { theActualData }, +}); +``` + +### When Returning Message Only + +```javascript +createApiResponse({ + message: 'A clear description of what succeeded', +}); +``` + +## 2. Error Responses (Status Codes 300+) + +Use a direct string message: + +```javascript +createApiResponse('A clear description of what went wrong', errorStatusCode); +``` + +## 3. Specific Patterns + +- Always include a descriptive message for successful operations +- Keep error messages concise and descriptive +- Use consistent status codes for similar types of errors +- Data should be structured as an object when including additional information + +## 4. Implementation Examples + +### Success with Data + +```javascript +return createApiResponse({ + message: 'Successfully retrieved crews', + data: { crews }, +}); +``` + +### Success with Message Only + +```javascript +return createApiResponse({ + message: `Successfully deleted ${key}`, +}); +``` + +### Error Response + +```javascript +return createApiResponse('Missing required parameter: address', 400); +``` + +## Example Responses + +Example Success Response: + +```json +{ + "success": true, + "data": { + "message": "Successfully retrieved crews", + "data": {} + } +} +``` + +Example Success Response (Message Only): + +```json +{ + "success": true, + "message": "Successfully deleted crew" +} +``` + +Example Error Response: + +```json +{ + "success": false, + "error": "Missing required parameter: address" +} +``` diff --git a/src/consistent_endpoints.md b/src/consistent_endpoints.md deleted file mode 100644 index 9a80d73..0000000 --- a/src/consistent_endpoints.md +++ /dev/null @@ -1,97 +0,0 @@ -# Implementation Guide: createApiResponse - -This guide outlines the standardized implementation of `createApiResponse` across all endpoints. - -## Response Formats - -### 1. Successful Responses (Status Codes 200-299) - -#### When Returning Data - -```javascript -createApiResponse({ - message: 'A clear description of what succeeded', - data: { theActualData } -}); -``` - -#### When Returning Message Only - -```javascript -createApiResponse({ - message: 'A clear description of what succeeded' -}); -``` - -### 2. Error Responses (Status Codes 300+) - -Use a direct string message: - -```javascript -createApiResponse('A clear description of what went wrong', errorStatusCode); -``` - -## Implementation Guidelines - -### 3. Specific Patterns - -- Always include a descriptive message for successful operations -- Keep error messages concise and descriptive -- Use consistent status codes for similar types of errors -- Data should be structured as an object when including additional information - -### 4. Implementation Examples - -#### Success with Data - -```javascript -return createApiResponse({ - message: 'Successfully retrieved crews', - data: { crews } -}); -``` - -#### Success with Message Only - -```javascript -return createApiResponse({ - message: `Successfully deleted ${key}` -}); -``` - -#### Error Response - -```javascript -return createApiResponse('Missing required parameter: address', 400); -``` - -## Response Structure - -The `createApiResponse` function automatically wraps responses in a standardized format that includes: - -- A success boolean flag (true for 200-299 status codes, false otherwise) -- For successful responses: - - data object containing message and any additional data -- For error responses: - - error string with the error message - -Example Success Response: -```json -{ - "success": true, - "data": { - "message": "Successfully retrieved crews", - "crews": [...] - } -} -``` - -Example Error Response: -```json -{ - "success": false, - "error": "Missing required parameter: address" -} -``` - -This standardization ensures consistent response structures across all endpoints and makes it easier for clients to handle responses predictably. From 8d0ebe24791e9694db6240513dd87a0eb82d80a3 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 01:06:53 -0700 Subject: [PATCH 056/128] fix: add handlers folder to split endpoints --- src/database/handlers/conversations.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/database/handlers/conversations.ts diff --git a/src/database/handlers/conversations.ts b/src/database/handlers/conversations.ts new file mode 100644 index 0000000..e69de29 From 2d54d5b616e0312cb0b095ba327c3c8b79395937 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 01:07:44 -0700 Subject: [PATCH 057/128] feat: Add functional handler system for conversations endpoints --- src/database/handlers/conversations.ts | 60 ++++++++++++++++++++++++++ src/database/handlers/index.ts | 12 ++++++ src/database/handlers/types.ts | 11 +++++ 3 files changed, 83 insertions(+) create mode 100644 src/database/handlers/index.ts create mode 100644 src/database/handlers/types.ts diff --git a/src/database/handlers/conversations.ts b/src/database/handlers/conversations.ts index e69de29..c8ce3e0 100644 --- a/src/database/handlers/conversations.ts +++ b/src/database/handlers/conversations.ts @@ -0,0 +1,60 @@ +import { Handler } from './types'; +import { createApiResponse } from '../../utils/requests-responses'; +import { getConversations, getLatestConversation, getConversationHistory, addConversation } from '../helpers/conversations'; +import { UserConversationsTable } from '../models'; + +export const handleConversations: Handler = async ({ orm, request, url }) => { + const endpoint = url.pathname.split('/').pop(); + + switch (endpoint) { + case 'conversations': + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const conversations = await getConversations(orm, address); + return createApiResponse({ + message: 'Successfully retrieved conversations', + data: conversations, + }); + + case 'latest': + const latestAddress = url.searchParams.get('address'); + if (!latestAddress) { + return createApiResponse('Missing address parameter', 400); + } + const conversation = await getLatestConversation(orm, latestAddress); + return createApiResponse({ + message: 'Successfully retrieved latest conversation', + data: conversation, + }); + + case 'history': + const conversationId = url.searchParams.get('id'); + if (!conversationId) { + return createApiResponse('Missing conversation ID parameter', 400); + } + const history = await getConversationHistory(orm, parseInt(conversationId)); + return createApiResponse({ + message: 'Successfully retrieved conversation history', + data: history, + }); + + case 'create': + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const { profile_id, conversation_name } = (await request.json()) as UserConversationsTable; + if (!profile_id) { + return createApiResponse('Missing required field: address', 400); + } + const result = await addConversation(orm, profile_id, conversation_name ? conversation_name : 'new conversation'); + return createApiResponse({ + message: 'Successfully created conversation', + data: { result }, + }); + + default: + return createApiResponse(`Unsupported conversations endpoint: ${endpoint}`, 404); + } +}; diff --git a/src/database/handlers/index.ts b/src/database/handlers/index.ts new file mode 100644 index 0000000..403a2ee --- /dev/null +++ b/src/database/handlers/index.ts @@ -0,0 +1,12 @@ +import { Handler } from './types'; +import { handleConversations } from './conversations'; + +const handlers: Record = { + conversations: handleConversations, + // Add more handlers as needed +}; + +export const getHandler = (path: string): Handler | undefined => { + const segment = path.split('/')[1]; + return handlers[segment]; +}; diff --git a/src/database/handlers/types.ts b/src/database/handlers/types.ts new file mode 100644 index 0000000..abbd265 --- /dev/null +++ b/src/database/handlers/types.ts @@ -0,0 +1,11 @@ +import { D1Orm } from 'd1-orm'; +import { Env } from '../../../worker-configuration'; + +export interface HandlerContext { + orm: D1Orm; + env: Env; + request: Request; + url: URL; +} + +export type Handler = (context: HandlerContext) => Promise; From a256860c09d987e16021db019b2cc8c81670c757 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 01:09:03 -0700 Subject: [PATCH 058/128] refactor: Migrate conversation endpoints to handler-based system --- src/database/database-do.ts | 62 +++++++------------------------------ 1 file changed, 11 insertions(+), 51 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 6a71881..debb8a6 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -2,6 +2,7 @@ import { DurableObject } from 'cloudflare:workers'; import { Env } from '../../worker-configuration'; import { AppConfig } from '../config'; import { createApiResponse, createUnsupportedEndpointResponse } from '../utils/requests-responses'; +import { getHandler } from './handlers'; import { D1Orm } from 'd1-orm'; import { UserAgentsTable, @@ -161,41 +162,17 @@ export class DatabaseDO extends DurableObject { } try { - // Conversation endpoints - if (endpoint === '/conversations') { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); + // Handle conversations endpoints using the new handler system + if (endpoint.startsWith('/conversations')) { + const handler = getHandler(endpoint); + if (handler) { + return handler({ + orm: this.orm, + env: this.env, + request, + url + }); } - const conversations = await getConversations(this.orm, address); - return createApiResponse({ - message: 'Successfully retrieved conversations', - data: conversations, - }); - } - - if (endpoint === '/conversations/latest') { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const conversation = await getLatestConversation(this.orm, address); - return createApiResponse({ - message: 'Successfully retrieved latest conversation', - data: conversation, - }); - } - - if (endpoint === '/conversations/history') { - const conversationId = url.searchParams.get('id'); - if (!conversationId) { - return createApiResponse('Missing conversation ID parameter', 400); - } - const history = await getConversationHistory(this.orm, parseInt(conversationId)); - return createApiResponse({ - message: 'Successfully retrieved conversation history', - data: history, - }); } // Crew endpoints @@ -586,23 +563,6 @@ export class DatabaseDO extends DurableObject { }); } - // Conversation creation endpoint - if (endpoint === '/conversations/create') { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - - const { profile_id, conversation_name } = (await request.json()) as UserConversationsTable; - if (!profile_id) { - return createApiResponse('Missing required field: address', 400); - } - - const result = await addConversation(this.orm, profile_id, conversation_name ? conversation_name : 'new conversation'); - return createApiResponse({ - message: 'Successfully updated user profile', - data: { result }, - }); - } // Profile endpoints if (endpoint === '/profiles/role') { From bc51bb5b91bfb372ba1c7b4ea0bebe6958bd6c34 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 01:22:15 -0700 Subject: [PATCH 059/128] refactor: Simplify conversation endpoint handling and remove unnecessary comments --- src/database/database-do.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index debb8a6..1a2926f 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -162,7 +162,7 @@ export class DatabaseDO extends DurableObject { } try { - // Handle conversations endpoints using the new handler system + // pass off to handler if (endpoint.startsWith('/conversations')) { const handler = getHandler(endpoint); if (handler) { @@ -170,7 +170,7 @@ export class DatabaseDO extends DurableObject { orm: this.orm, env: this.env, request, - url + url, }); } } @@ -563,7 +563,6 @@ export class DatabaseDO extends DurableObject { }); } - // Profile endpoints if (endpoint === '/profiles/role') { const address = url.searchParams.get('address'); From fc1ddd3e281a124dcd7be9f48f63fd6ec0aa1c2a Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 01:22:19 -0700 Subject: [PATCH 060/128] feat: Add crews handler with profile, public, get, create, update, and delete endpoints --- src/database/database-do.ts | 102 +++-------------------------- src/database/handlers/crews.ts | 115 +++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 92 deletions(-) create mode 100644 src/database/handlers/crews.ts diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 1a2926f..af012de 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -175,99 +175,17 @@ export class DatabaseDO extends DurableObject { } } - // Crew endpoints - if (endpoint === '/crews/profile') { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - - // Get the session token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader) { - return createApiResponse('Missing authorization header', 401); - } - - // Extract token from Bearer format - const token = authHeader.replace('Bearer ', ''); - - // Verify the token matches the requested address - const tokenAddress = await validateSessionToken(this.env, token); - if (!tokenAddress.success || tokenAddress.address !== address) { - return createApiResponse('Unauthorized access', 403); - } - - const crews = await getCrewsByProfile(this.orm, address); - return createApiResponse({ - message: 'Successfully retrieved profile crews', - data: { crews }, - }); - } - - if (endpoint === '/crews/public') { - const crews = await getPublicCrews(this.orm); - return createApiResponse({ - message: 'Successfully retrieved public crews', - data: { crews }, - }); - } - - if (endpoint === '/crews/get') { - const crewId = url.searchParams.get('id'); - if (!crewId) { - return createApiResponse('Missing id parameter', 400); - } - const crew = await getCrew(this.orm, parseInt(crewId)); - return createApiResponse({ - message: 'Successfully retrieved crew', - data: { crew }, - }); - } - - if (endpoint === '/crews/create') { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const crewData = (await request.json()) as Omit; - if (!crewData.profile_id || !crewData.crew_name) { - return createApiResponse('Missing required fields: profile_id, crew_name', 400); - } - const crew = await createCrew(this.orm, crewData); - return createApiResponse({ - message: 'Successfully created crew', - data: { crew }, - }); - } - - if (endpoint === '/crews/update') { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const crewId = url.searchParams.get('id'); - if (!crewId) { - return createApiResponse('Missing id parameter', 400); - } - const updates = (await request.json()) as Partial>; - const result = await updateCrew(this.orm, parseInt(crewId), updates); - return createApiResponse({ - message: 'Successfully updated crew', - data: { result }, - }); - } - - if (endpoint === '/crews/delete') { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const crewId = url.searchParams.get('id'); - if (!crewId) { - return createApiResponse('Missing id parameter', 400); + // pass off to handler + if (endpoint.startsWith('/crews')) { + const handler = getHandler(endpoint); + if (handler) { + return handler({ + orm: this.orm, + env: this.env, + request, + url, + }); } - const result = await deleteCrew(this.orm, parseInt(crewId)); - return createApiResponse({ - message: 'Successfully deleted crew', - data: { result }, - }); } // Agent endpoints diff --git a/src/database/handlers/crews.ts b/src/database/handlers/crews.ts new file mode 100644 index 0000000..9c3fcc7 --- /dev/null +++ b/src/database/handlers/crews.ts @@ -0,0 +1,115 @@ +import { Handler } from './types'; +import { createApiResponse } from '../../utils/requests-responses'; +import { validateSessionToken } from '../../utils/auth-helper'; +import { + getCrewsByProfile, + getPublicCrews, + getCrew, + createCrew, + updateCrew, + deleteCrew +} from '../helpers/crews'; +import { UserCrewsTable } from '../models'; + +export const handleCrews: Handler = async ({ orm, env, request, url }) => { + const endpoint = url.pathname.split('/').pop(); + + switch (endpoint) { + case 'profile': { + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + + // Get the session token from Authorization header + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + + // Extract token from Bearer format + const token = authHeader.replace('Bearer ', ''); + + // Verify the token matches the requested address + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success || tokenAddress.address !== address) { + return createApiResponse('Unauthorized access', 403); + } + + const crews = await getCrewsByProfile(orm, address); + return createApiResponse({ + message: 'Successfully retrieved profile crews', + data: { crews }, + }); + } + + case 'public': { + const crews = await getPublicCrews(orm); + return createApiResponse({ + message: 'Successfully retrieved public crews', + data: { crews }, + }); + } + + case 'get': { + const crewId = url.searchParams.get('id'); + if (!crewId) { + return createApiResponse('Missing id parameter', 400); + } + const crew = await getCrew(orm, parseInt(crewId)); + return createApiResponse({ + message: 'Successfully retrieved crew', + data: { crew }, + }); + } + + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const crewData = (await request.json()) as Omit; + if (!crewData.profile_id || !crewData.crew_name) { + return createApiResponse('Missing required fields: profile_id, crew_name', 400); + } + const crew = await createCrew(orm, crewData); + return createApiResponse({ + message: 'Successfully created crew', + data: { crew }, + }); + } + + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const crewId = url.searchParams.get('id'); + if (!crewId) { + return createApiResponse('Missing id parameter', 400); + } + const updates = (await request.json()) as Partial>; + const result = await updateCrew(orm, parseInt(crewId), updates); + return createApiResponse({ + message: 'Successfully updated crew', + data: { result }, + }); + } + + case 'delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const crewId = url.searchParams.get('id'); + if (!crewId) { + return createApiResponse('Missing id parameter', 400); + } + const result = await deleteCrew(orm, parseInt(crewId)); + return createApiResponse({ + message: 'Successfully deleted crew', + data: { result }, + }); + } + + default: + return createApiResponse(`Unsupported crews endpoint: ${endpoint}`, 404); + } +}; From 18a5e4a3b14c36bcd178fc076f39cc6be4474786 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 01:23:22 -0700 Subject: [PATCH 061/128] feat: Add agents handler with CRUD operations --- src/database/database-do.ts | 78 ++++------------------------- src/database/handlers/agents.ts | 89 +++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 68 deletions(-) create mode 100644 src/database/handlers/agents.ts diff --git a/src/database/database-do.ts b/src/database/database-do.ts index af012de..a674d7a 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -188,75 +188,17 @@ export class DatabaseDO extends DurableObject { } } - // Agent endpoints - if (endpoint === '/agents/get') { - const crewId = url.searchParams.get('crewId'); - if (!crewId) { - return createApiResponse('Missing crewId parameter', 400); - } - const agents = await getAgents(this.orm, parseInt(crewId)); - return createApiResponse({ - message: 'Successfully retrieved agents', - data: { agents }, - }); - } - - if (endpoint === '/agents/create') { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const agentData = (await request.json()) as Omit; - if ( - !agentData.profile_id || - !agentData.crew_id || - !agentData.agent_name || - !agentData.agent_role || - !agentData.agent_goal || - !agentData.agent_backstory - ) { - return createApiResponse( - 'Missing required fields: profile_id, crew_id, agent_name, agent_role, agent_goal, agent_backstory', - 400 - ); - } - const agent = await createAgent(this.orm, agentData); - return createApiResponse({ - message: 'Successfully created agent', - data: { agent }, - }); - } - - if (endpoint === '/agents/update') { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const agentId = url.searchParams.get('id'); - if (!agentId) { - return createApiResponse('Missing id parameter', 400); - } - const updates = (await request.json()) as Partial< - Omit - >; - const result = await updateAgent(this.orm, parseInt(agentId), updates); - return createApiResponse({ - message: 'Successfully updated agent', - data: { result }, - }); - } - - if (endpoint === '/agents/delete') { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const agentId = url.searchParams.get('id'); - if (!agentId) { - return createApiResponse('Missing id parameter', 400); + // pass off to handler + if (endpoint.startsWith('/agents')) { + const handler = getHandler(endpoint); + if (handler) { + return handler({ + orm: this.orm, + env: this.env, + request, + url, + }); } - const result = await deleteAgent(this.orm, parseInt(agentId)); - return createApiResponse({ - message: 'Successfully deleted agent', - data: { result }, - }); } // Task endpoints diff --git a/src/database/handlers/agents.ts b/src/database/handlers/agents.ts new file mode 100644 index 0000000..88f0105 --- /dev/null +++ b/src/database/handlers/agents.ts @@ -0,0 +1,89 @@ +import { Handler } from './types'; +import { createApiResponse } from '../../utils/requests-responses'; +import { validateSessionToken } from '../../utils/auth-helper'; +import { + getAgents, + createAgent, + updateAgent, + deleteAgent +} from '../helpers/agents'; +import { UserAgentsTable } from '../models'; + +export const handleAgents: Handler = async ({ orm, env, request, url }) => { + const endpoint = url.pathname.split('/').pop(); + + switch (endpoint) { + case 'get': { + const crewId = url.searchParams.get('crewId'); + if (!crewId) { + return createApiResponse('Missing crewId parameter', 400); + } + const agents = await getAgents(orm, parseInt(crewId)); + return createApiResponse({ + message: 'Successfully retrieved agents', + data: { agents }, + }); + } + + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const agentData = (await request.json()) as Omit; + if ( + !agentData.profile_id || + !agentData.crew_id || + !agentData.agent_name || + !agentData.agent_role || + !agentData.agent_goal || + !agentData.agent_backstory + ) { + return createApiResponse( + 'Missing required fields: profile_id, crew_id, agent_name, agent_role, agent_goal, agent_backstory', + 400 + ); + } + const agent = await createAgent(orm, agentData); + return createApiResponse({ + message: 'Successfully created agent', + data: { agent }, + }); + } + + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const agentId = url.searchParams.get('id'); + if (!agentId) { + return createApiResponse('Missing id parameter', 400); + } + const updates = (await request.json()) as Partial< + Omit + >; + const result = await updateAgent(orm, parseInt(agentId), updates); + return createApiResponse({ + message: 'Successfully updated agent', + data: { result }, + }); + } + + case 'delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const agentId = url.searchParams.get('id'); + if (!agentId) { + return createApiResponse('Missing id parameter', 400); + } + const result = await deleteAgent(orm, parseInt(agentId)); + return createApiResponse({ + message: 'Successfully deleted agent', + data: { result }, + }); + } + + default: + return createApiResponse(`Unsupported agents endpoint: ${endpoint}`, 404); + } +}; From 32df66cc2802d9d61802bc31865cc1765375ea12 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 01:25:01 -0700 Subject: [PATCH 062/128] refactor: Remove redundant comments in endpoint handling logic --- src/database/database-do.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index a674d7a..159273b 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -175,7 +175,6 @@ export class DatabaseDO extends DurableObject { } } - // pass off to handler if (endpoint.startsWith('/crews')) { const handler = getHandler(endpoint); if (handler) { @@ -188,7 +187,6 @@ export class DatabaseDO extends DurableObject { } } - // pass off to handler if (endpoint.startsWith('/agents')) { const handler = getHandler(endpoint); if (handler) { From d49acb3b5af2d791272a4af5d454c60672f5cb55 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 01:25:04 -0700 Subject: [PATCH 063/128] feat: Add tasks handler with CRUD operations and routing --- src/database/database-do.ts | 109 ++++-------------------------- src/database/handlers/tasks.ts | 118 +++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 97 deletions(-) create mode 100644 src/database/handlers/tasks.ts diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 159273b..1859037 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -199,103 +199,18 @@ export class DatabaseDO extends DurableObject { } } - // Task endpoints - if (endpoint === '/tasks/get') { - const taskId = url.searchParams.get('id'); - if (!taskId) { - return createApiResponse('Missing id parameter', 400); - } - const task = await getTask(this.orm, parseInt(taskId)); - return createApiResponse({ - message: 'Successfully retrieved task', - data: { task }, - }); - } - - if (endpoint === '/tasks/list') { - const agentId = url.searchParams.get('agentId'); - if (!agentId) { - return createApiResponse('Missing agentId parameter', 400); - } - const tasks = await getTasks(this.orm, parseInt(agentId)); - return createApiResponse({ - message: 'Successfully retrieved tasks', - data: { tasks }, - }); - } - - if (endpoint === '/tasks/create') { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const taskData = (await request.json()) as UserTasksTable; - if ( - !taskData.profile_id || - !taskData.crew_id || - !taskData.agent_id || - !taskData.task_name || - !taskData.task_description || - !taskData.task_expected_output - ) { - return createApiResponse( - 'Missing required fields: profile_id, crew_id, agent_id, task_name, task_description, task_expected_output', - 400 - ); - } - const task = await createTask(this.orm, taskData); - return createApiResponse({ - message: 'Successfully created task', - data: { task }, - }); - } - - if (endpoint === '/tasks/update') { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const taskId = url.searchParams.get('id'); - if (!taskId) { - return createApiResponse('Missing id parameter', 400); - } - const updates = (await request.json()) as Partial< - Omit - >; - const result = await updateTask(this.orm, parseInt(taskId), updates); - return createApiResponse({ - message: 'Successfully updated task', - data: { result }, - }); - } - - if (endpoint === '/tasks/delete') { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const taskId = url.searchParams.get('id'); - if (!taskId) { - return createApiResponse('Missing id parameter', 400); - } - const result = await deleteTask(this.orm, parseInt(taskId)); - return createApiResponse({ - message: 'Successfully deleted task', - data: { result }, - }); - } - - if (endpoint === '/tasks/delete-all') { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const agentId = url.searchParams.get('agentId'); - if (!agentId) { - return createApiResponse('Missing agentId parameter', 400); - } - const result = await deleteTasks(this.orm, parseInt(agentId)); - return createApiResponse({ - message: 'Successfully deleted all tasks for agent', - data: { result }, - }); - } + // Pass off to tasks handler + if (endpoint.startsWith('/tasks')) { + const handler = getHandler(endpoint); + if (handler) { + return handler({ + orm: this.orm, + env: this.env, + request, + url, + }); + } + } if (endpoint === '/crews/executions') { const address = url.searchParams.get('address'); diff --git a/src/database/handlers/tasks.ts b/src/database/handlers/tasks.ts new file mode 100644 index 0000000..bf61923 --- /dev/null +++ b/src/database/handlers/tasks.ts @@ -0,0 +1,118 @@ +import { Handler } from './types'; +import { createApiResponse } from '../../utils/requests-responses'; +import { validateSessionToken } from '../../utils/auth-helper'; +import { + getTask, + getTasks, + createTask, + updateTask, + deleteTask, + deleteTasks +} from '../helpers/tasks'; +import { UserTasksTable } from '../models'; + +export const handleTasks: Handler = async ({ orm, env, request, url }) => { + const endpoint = url.pathname.split('/').pop(); + + switch (endpoint) { + case 'get': { + const taskId = url.searchParams.get('id'); + if (!taskId) { + return createApiResponse('Missing id parameter', 400); + } + const task = await getTask(orm, parseInt(taskId)); + return createApiResponse({ + message: 'Successfully retrieved task', + data: { task }, + }); + } + + case 'list': { + const agentId = url.searchParams.get('agentId'); + if (!agentId) { + return createApiResponse('Missing agentId parameter', 400); + } + const tasks = await getTasks(orm, parseInt(agentId)); + return createApiResponse({ + message: 'Successfully retrieved tasks', + data: { tasks }, + }); + } + + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const taskData = (await request.json()) as UserTasksTable; + if ( + !taskData.profile_id || + !taskData.crew_id || + !taskData.agent_id || + !taskData.task_name || + !taskData.task_description || + !taskData.task_expected_output + ) { + return createApiResponse( + 'Missing required fields: profile_id, crew_id, agent_id, task_name, task_description, task_expected_output', + 400 + ); + } + const task = await createTask(orm, taskData); + return createApiResponse({ + message: 'Successfully created task', + data: { task }, + }); + } + + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const taskId = url.searchParams.get('id'); + if (!taskId) { + return createApiResponse('Missing id parameter', 400); + } + const updates = (await request.json()) as Partial< + Omit + >; + const result = await updateTask(orm, parseInt(taskId), updates); + return createApiResponse({ + message: 'Successfully updated task', + data: { result }, + }); + } + + case 'delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const taskId = url.searchParams.get('id'); + if (!taskId) { + return createApiResponse('Missing id parameter', 400); + } + const result = await deleteTask(orm, parseInt(taskId)); + return createApiResponse({ + message: 'Successfully deleted task', + data: { result }, + }); + } + + case 'delete-all': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const agentId = url.searchParams.get('agentId'); + if (!agentId) { + return createApiResponse('Missing agentId parameter', 400); + } + const result = await deleteTasks(orm, parseInt(agentId)); + return createApiResponse({ + message: 'Successfully deleted all tasks for agent', + data: { result }, + }); + } + + default: + return createApiResponse(`Unsupported tasks endpoint: ${endpoint}`, 404); + } +}; From b0e6f1b6bc1e0d8cc3cfa3022979dc9db67bdb3a Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 01:26:11 -0700 Subject: [PATCH 064/128] refactor: Improve code formatting and indentation in database-do.ts --- src/database/database-do.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 1859037..f08c087 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -199,18 +199,18 @@ export class DatabaseDO extends DurableObject { } } - // Pass off to tasks handler - if (endpoint.startsWith('/tasks')) { - const handler = getHandler(endpoint); - if (handler) { - return handler({ - orm: this.orm, - env: this.env, - request, - url, - }); - } - } + // Pass off to tasks handler + if (endpoint.startsWith('/tasks')) { + const handler = getHandler(endpoint); + if (handler) { + return handler({ + orm: this.orm, + env: this.env, + request, + url, + }); + } + } if (endpoint === '/crews/executions') { const address = url.searchParams.get('address'); From df50f340a9c45a14c58140de408b527940d1ca60 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 01:26:13 -0700 Subject: [PATCH 065/128] feat: Add crons handler and update database-do to use handler-based routing --- src/database/database-do.ts | 93 ++++------------------------- src/database/handlers/crons.ts | 105 +++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 83 deletions(-) create mode 100644 src/database/handlers/crons.ts diff --git a/src/database/database-do.ts b/src/database/database-do.ts index f08c087..4adeb6d 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -250,90 +250,17 @@ export class DatabaseDO extends DurableObject { }); } - // Cron endpoints - if (endpoint === '/crons/enabled') { - const crons = await getEnabledCrons(this.orm); - return createApiResponse({ - message: 'Successfully retrieved enabled crons', - data: { crons }, - }); - } - - if (endpoint === '/crons/enabled/detailed') { - const crons = await getEnabledCronsDetailed(this.orm); - return createApiResponse({ - message: 'Successfully retrieved detailed cron information', - data: { crons }, - }); - } - - // Cron management endpoints - if (endpoint === '/crons/get') { - const crewId = url.searchParams.get('crewId'); - if (!crewId) { - return createApiResponse('Missing crewId parameter', 400); - } - const crons = await getCronsByCrew(this.orm, parseInt(crewId)); - return createApiResponse({ - message: 'Successfully retrieved crons for crew', - data: { crons }, - }); - } - - if (endpoint === '/crons/create') { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const cronData = (await request.json()) as UserCronsTable; - if (!cronData.profile_id || !cronData.crew_id || cronData.cron_enabled === undefined) { - return createApiResponse('Missing required fields: profile_id, crew_id, cron_enabled', 400); - } - // Set defaults if not provided - cronData.cron_interval = cronData.cron_interval || '0 * * * *'; // Default to hourly - cronData.cron_input = cronData.cron_input || ''; - const cron = await createCron(this.orm, cronData); - return createApiResponse({ - message: 'Successfully created cron', - data: { cron }, - }); - } - - if (endpoint === '/crons/update') { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const cronId = url.searchParams.get('id'); - if (!cronId) { - return createApiResponse('Missing id parameter', 400); - } - const { cron_input } = (await request.json()) as UserCronsTable; - if (cron_input === undefined) { - return createApiResponse('Missing cron_input in request body', 400); - } - const result = await updateCronInput(this.orm, parseInt(cronId), cron_input); - return createApiResponse({ - message: 'Successfully updated user profile', - data: { result }, - }); - } - - if (endpoint === '/crons/toggle') { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const cronId = url.searchParams.get('id'); - if (!cronId) { - return createApiResponse('Missing id parameter', 400); - } - const { cron_enabled } = (await request.json()) as UserCronsTable; - if (cron_enabled === undefined) { - return createApiResponse('Missing cron_enabled in request body', 400); + // Pass off to crons handler + if (endpoint.startsWith('/crons')) { + const handler = getHandler(endpoint); + if (handler) { + return handler({ + orm: this.orm, + env: this.env, + request, + url, + }); } - const result = await toggleCronStatus(this.orm, parseInt(cronId), cron_enabled ? 1 : 0); - return createApiResponse({ - message: 'Successfully toggled cron status', - data: { result }, - }); } // Profile endpoints diff --git a/src/database/handlers/crons.ts b/src/database/handlers/crons.ts new file mode 100644 index 0000000..1099040 --- /dev/null +++ b/src/database/handlers/crons.ts @@ -0,0 +1,105 @@ +import { Handler } from './types'; +import { createApiResponse } from '../../utils/requests-responses'; +import { validateSessionToken } from '../../utils/auth-helper'; +import { + getCronsByCrew, + createCron, + updateCronInput, + toggleCronStatus, + getEnabledCrons, + getEnabledCronsDetailed +} from '../helpers/crons'; +import { UserCronsTable } from '../models'; + +export const handleCrons: Handler = async ({ orm, env, request, url }) => { + const endpoint = url.pathname.split('/').pop(); + + switch (endpoint) { + case 'enabled': { + const crons = await getEnabledCrons(orm); + return createApiResponse({ + message: 'Successfully retrieved enabled crons', + data: { crons }, + }); + } + + case 'enabled-detailed': { + const crons = await getEnabledCronsDetailed(orm); + return createApiResponse({ + message: 'Successfully retrieved detailed cron information', + data: { crons }, + }); + } + + case 'get': { + const crewId = url.searchParams.get('crewId'); + if (!crewId) { + return createApiResponse('Missing crewId parameter', 400); + } + const crons = await getCronsByCrew(orm, parseInt(crewId)); + return createApiResponse({ + message: 'Successfully retrieved crons for crew', + data: { crons }, + }); + } + + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const cronData = (await request.json()) as UserCronsTable; + if (!cronData.profile_id || !cronData.crew_id || cronData.cron_enabled === undefined) { + return createApiResponse('Missing required fields: profile_id, crew_id, cron_enabled', 400); + } + // Set defaults if not provided + cronData.cron_interval = cronData.cron_interval || '0 * * * *'; // Default to hourly + cronData.cron_input = cronData.cron_input || ''; + const cron = await createCron(orm, cronData); + return createApiResponse({ + message: 'Successfully created cron', + data: { cron }, + }); + } + + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const cronId = url.searchParams.get('id'); + if (!cronId) { + return createApiResponse('Missing id parameter', 400); + } + const { cron_input } = (await request.json()) as UserCronsTable; + if (cron_input === undefined) { + return createApiResponse('Missing cron_input in request body', 400); + } + const result = await updateCronInput(orm, parseInt(cronId), cron_input); + return createApiResponse({ + message: 'Successfully updated cron input', + data: { result }, + }); + } + + case 'toggle': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const cronId = url.searchParams.get('id'); + if (!cronId) { + return createApiResponse('Missing id parameter', 400); + } + const { cron_enabled } = (await request.json()) as UserCronsTable; + if (cron_enabled === undefined) { + return createApiResponse('Missing cron_enabled in request body', 400); + } + const result = await toggleCronStatus(orm, parseInt(cronId), cron_enabled ? 1 : 0); + return createApiResponse({ + message: 'Successfully toggled cron status', + data: { result }, + }); + } + + default: + return createApiResponse(`Unsupported crons endpoint: ${endpoint}`, 404); + } +}; From 1db1b059b4b644f4cb6cb6fae591338c045b3f9b Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 01:27:31 -0700 Subject: [PATCH 066/128] feat: Add crew executions and steps handling to crews handler --- src/database/database-do.ts | 134 ------------------------------ src/database/handlers/crews.ts | 144 ++++++++++++++++++++++++++++++++- 2 files changed, 142 insertions(+), 136 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 4adeb6d..7435da7 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -212,43 +212,6 @@ export class DatabaseDO extends DurableObject { } } - if (endpoint === '/crews/executions') { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const executions = await getCrewExecutions(this.orm, address); - return createApiResponse({ - message: 'Successfully retrieved crew executions', - data: { executions }, - }); - } - - if (endpoint === '/crews/executions/add') { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - - type AddCrewExecutionRequest = { - address: string; - crewId: number; - conversationId: number; - input: string; - }; - - const body: AddCrewExecutionRequest = await request.json(); - const { address, crewId, conversationId, input } = body; - - if (!address || !crewId || !conversationId || !input) { - return createApiResponse('Missing required parameters: address, crewId, conversationId, input', 400); - } - - const execution = await addCrewExecution(this.orm, address, crewId, conversationId, input); - return createApiResponse({ - message: 'Successfully created crew execution', - data: { execution }, - }); - } // Pass off to crons handler if (endpoint.startsWith('/crons')) { @@ -477,103 +440,6 @@ export class DatabaseDO extends DurableObject { }); } - // Crew execution steps endpoints - if (endpoint === '/crews/steps/get') { - const executionId = url.searchParams.get('executionId'); - if (!executionId) { - return createApiResponse('Missing executionId parameter', 400); - } - - // Get the session token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader) { - return createApiResponse('Missing authorization header', 401); - } - - // Extract token from Bearer format - const token = authHeader.replace('Bearer ', ''); - - // Verify the token matches the requested address - const tokenAddress = await validateSessionToken(this.env, token); - if (!tokenAddress.success) { - return createApiResponse('Unauthorized access', 403); - } - - const steps = await getExecutionSteps(this.orm, parseInt(executionId)); - return createApiResponse({ - message: 'Successfully retrieved execution steps', - data: { steps }, - }); - } - - if (endpoint === '/crews/steps/create') { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - - // Get the session token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader) { - return createApiResponse('Missing authorization header', 401); - } - - // Extract token from Bearer format - const token = authHeader.replace('Bearer ', ''); - - // Verify the token - const tokenAddress = await validateSessionToken(this.env, token); - if (!tokenAddress) { - return createApiResponse('Unauthorized access', 403); - } - - const stepData = (await request.json()) as UserCrewExecutionStepsTable; - if (!stepData.profile_id || !stepData.crew_id || !stepData.execution_id || !stepData.step_type || !stepData.step_data) { - return createApiResponse('Missing required fields: profile_id, crew_id, cron_enabled', 400); - } - - // Verify the profile_id matches the token address - if (stepData.profile_id !== tokenAddress.address) { - return createApiResponse('Unauthorized: profile_id does not match token', 403); - } - - const step = await createExecutionStep(this.orm, stepData); - return createApiResponse({ - message: 'Successfully created execution step', - data: { step }, - }); - } - - if (endpoint === '/crews/steps/delete') { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - - const executionId = url.searchParams.get('executionId'); - if (!executionId) { - return createApiResponse('Missing executionId parameter', 400); - } - - // Get the session token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader) { - return createApiResponse('Missing authorization header', 401); - } - - // Extract token from Bearer format - const token = authHeader.replace('Bearer ', ''); - - // Verify the token - const tokenAddress = await validateSessionToken(this.env, token); - if (!tokenAddress) { - return createApiResponse('Unauthorized access', 403); - } - - const result = await deleteExecutionSteps(this.orm, parseInt(executionId)); - return createApiResponse({ - message: 'Successfully deleted execution steps', - data: { result }, - }); - } return createUnsupportedEndpointResponse(endpoint, this.SUPPORTED_ENDPOINTS); } diff --git a/src/database/handlers/crews.ts b/src/database/handlers/crews.ts index 9c3fcc7..096fb77 100644 --- a/src/database/handlers/crews.ts +++ b/src/database/handlers/crews.ts @@ -7,9 +7,14 @@ import { getCrew, createCrew, updateCrew, - deleteCrew + deleteCrew, + getCrewExecutions, + addCrewExecution, + getExecutionSteps, + createExecutionStep, + deleteExecutionSteps } from '../helpers/crews'; -import { UserCrewsTable } from '../models'; +import { UserCrewsTable, UserCrewExecutionStepsTable } from '../models'; export const handleCrews: Handler = async ({ orm, env, request, url }) => { const endpoint = url.pathname.split('/').pop(); @@ -109,6 +114,141 @@ export const handleCrews: Handler = async ({ orm, env, request, url }) => { }); } + case 'executions': { + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const executions = await getCrewExecutions(orm, address); + return createApiResponse({ + message: 'Successfully retrieved crew executions', + data: { executions }, + }); + } + + case 'executions/add': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + + type AddCrewExecutionRequest = { + address: string; + crewId: number; + conversationId: number; + input: string; + }; + + const body: AddCrewExecutionRequest = await request.json(); + const { address, crewId, conversationId, input } = body; + + if (!address || !crewId || !conversationId || !input) { + return createApiResponse('Missing required parameters: address, crewId, conversationId, input', 400); + } + + const execution = await addCrewExecution(orm, address, crewId, conversationId, input); + return createApiResponse({ + message: 'Successfully created crew execution', + data: { execution }, + }); + } + + case 'steps/get': { + const executionId = url.searchParams.get('executionId'); + if (!executionId) { + return createApiResponse('Missing executionId parameter', 400); + } + + // Get the session token from Authorization header + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + + // Extract token from Bearer format + const token = authHeader.replace('Bearer ', ''); + + // Verify the token matches the requested address + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success) { + return createApiResponse('Unauthorized access', 403); + } + + const steps = await getExecutionSteps(orm, parseInt(executionId)); + return createApiResponse({ + message: 'Successfully retrieved execution steps', + data: { steps }, + }); + } + + case 'steps/create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + + // Get the session token from Authorization header + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + + // Extract token from Bearer format + const token = authHeader.replace('Bearer ', ''); + + // Verify the token + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success) { + return createApiResponse('Unauthorized access', 403); + } + + const stepData = (await request.json()) as UserCrewExecutionStepsTable; + if (!stepData.profile_id || !stepData.crew_id || !stepData.execution_id || !stepData.step_type || !stepData.step_data) { + return createApiResponse('Missing required fields: profile_id, crew_id, execution_id, step_type, step_data', 400); + } + + // Verify the profile_id matches the token address + if (stepData.profile_id !== tokenAddress.address) { + return createApiResponse('Unauthorized: profile_id does not match token', 403); + } + + const step = await createExecutionStep(orm, stepData); + return createApiResponse({ + message: 'Successfully created execution step', + data: { step }, + }); + } + + case 'steps/delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + + const executionId = url.searchParams.get('executionId'); + if (!executionId) { + return createApiResponse('Missing executionId parameter', 400); + } + + // Get the session token from Authorization header + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + + // Extract token from Bearer format + const token = authHeader.replace('Bearer ', ''); + + // Verify the token + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success) { + return createApiResponse('Unauthorized access', 403); + } + + const result = await deleteExecutionSteps(orm, parseInt(executionId)); + return createApiResponse({ + message: 'Successfully deleted execution steps', + data: { result }, + }); + } + default: return createApiResponse(`Unsupported crews endpoint: ${endpoint}`, 404); } From 8437d62c5162910efe5461a273b4fda6d4de9e7b Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 01:28:53 -0700 Subject: [PATCH 067/128] feat: Add profiles handler for centralized profile management --- src/database/database-do.ts | 103 +++----------------------- src/database/handlers/profiles.ts | 115 ++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 93 deletions(-) create mode 100644 src/database/handlers/profiles.ts diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 7435da7..ea135dc 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -226,100 +226,17 @@ export class DatabaseDO extends DurableObject { } } - // Profile endpoints - if (endpoint === '/profiles/role') { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const role = await getUserRole(this.orm, address); - return createApiResponse({ - message: 'Successfully retrieved user role', - data: { role }, - }); - } - - if (endpoint === '/profiles/get') { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const profile = await getUserProfile(this.orm, address); - return createApiResponse({ - message: 'Successfully retrieved user profile', - data: { profile }, - }); - } - - if (endpoint === '/profiles/create') { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const profileData = (await request.json()) as UserProfilesTable; - if (!profileData.stx_address || !profileData.user_role) { - return createApiResponse('Missing required fields: stx_address, user_role', 400); - } - const profile = await createUserProfile(this.orm, profileData); - return createApiResponse({ - message: 'Successfully created user profile', - data: { profile }, - }); - } - - if (endpoint === '/profiles/update') { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const profileData = (await request.json()) as UserProfilesTable; - const result = await updateUserProfile(this.orm, address, profileData); - return createApiResponse({ - message: 'Successfully updated user profile', - data: { result }, - }); - } - - if (endpoint === '/profiles/delete') { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const result = await deleteUserProfile(this.orm, address); - return createApiResponse({ - message: 'Successfully deleted user profile', - data: { result }, - }); - } - - // Admin profile endpoints - if (endpoint === '/profiles/admin/list') { - const profiles = await getAllUserProfiles(this.orm); - return createApiResponse({ - message: 'Successfully retrieved all user profiles', - data: { profiles }, - }); - } - - if (endpoint === '/profiles/admin/update') { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const userId = url.searchParams.get('userId'); - if (!userId) { - return createApiResponse('Missing userId parameter', 400); + // Pass off to profiles handler + if (endpoint.startsWith('/profiles')) { + const handler = getHandler(endpoint); + if (handler) { + return handler({ + orm: this.orm, + env: this.env, + request, + url, + }); } - const updates = (await request.json()) as UserProfilesTable; - const result = await updateUserProfileById(this.orm, parseInt(userId), updates); - return createApiResponse({ - message: 'Successfully updated user profile', - data: { result }, - }); } } catch (error) { console.error(`Database error: ${error instanceof Error ? error.message : String(error)}`); diff --git a/src/database/handlers/profiles.ts b/src/database/handlers/profiles.ts new file mode 100644 index 0000000..1b75bf5 --- /dev/null +++ b/src/database/handlers/profiles.ts @@ -0,0 +1,115 @@ +import { Handler } from './types'; +import { createApiResponse } from '../../utils/requests-responses'; +import { + getUserRole, + getUserProfile, + createUserProfile, + updateUserProfile, + deleteUserProfile, + getAllUserProfiles, + updateUserProfileById +} from '../helpers/profiles'; +import { UserProfilesTable } from '../models'; + +export const handleProfiles: Handler = async ({ orm, env, request, url }) => { + const endpoint = url.pathname.split('/').pop(); + + switch (endpoint) { + case 'role': { + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const role = await getUserRole(orm, address); + return createApiResponse({ + message: 'Successfully retrieved user role', + data: { role }, + }); + } + + case 'get': { + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const profile = await getUserProfile(orm, address); + return createApiResponse({ + message: 'Successfully retrieved user profile', + data: { profile }, + }); + } + + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const profileData = (await request.json()) as UserProfilesTable; + if (!profileData.stx_address || !profileData.user_role) { + return createApiResponse('Missing required fields: stx_address, user_role', 400); + } + const profile = await createUserProfile(orm, profileData); + return createApiResponse({ + message: 'Successfully created user profile', + data: { profile }, + }); + } + + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const profileData = (await request.json()) as UserProfilesTable; + const result = await updateUserProfile(orm, address, profileData); + return createApiResponse({ + message: 'Successfully updated user profile', + data: { result }, + }); + } + + case 'delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const result = await deleteUserProfile(orm, address); + return createApiResponse({ + message: 'Successfully deleted user profile', + data: { result }, + }); + } + + case 'list': { + const profiles = await getAllUserProfiles(orm); + return createApiResponse({ + message: 'Successfully retrieved all user profiles', + data: { profiles }, + }); + } + + case 'admin-update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const userId = url.searchParams.get('userId'); + if (!userId) { + return createApiResponse('Missing userId parameter', 400); + } + const updates = (await request.json()) as UserProfilesTable; + const result = await updateUserProfileById(orm, parseInt(userId), updates); + return createApiResponse({ + message: 'Successfully updated user profile', + data: { result }, + }); + } + + default: + return createApiResponse(`Unsupported profiles endpoint: ${endpoint}`, 404); + } +}; From db92bb6a2cffb5d35405a88ade145e8abf458a68 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 01:30:01 -0700 Subject: [PATCH 068/128] feat: Add Twitter handler for X Bot endpoints --- src/database/database-do.ts | 122 +++------------------------ src/database/handlers/twitter.ts | 136 +++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 112 deletions(-) create mode 100644 src/database/handlers/twitter.ts diff --git a/src/database/database-do.ts b/src/database/database-do.ts index ea135dc..837f181 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -243,121 +243,19 @@ export class DatabaseDO extends DurableObject { return createApiResponse(`Database error: ${error instanceof Error ? error.message : String(error)}`, 500); } - // Twitter/X Bot endpoints - if (endpoint === '/twitter/authors/get') { - const authorId = url.searchParams.get('authorId'); - if (!authorId) { - return createApiResponse('Missing authorId parameter', 400); + // Pass off to twitter handler + if (endpoint.startsWith('/twitter')) { + const handler = getHandler(endpoint); + if (handler) { + return handler({ + orm: this.orm, + env: this.env, + request, + url, + }); } - const author = await getAuthor(this.orm, authorId); - return createApiResponse({ - message: 'Successfully retrieved author', - data: { author }, - }); } - if (endpoint === '/twitter/authors/create') { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const { author_id, realname, username } = (await request.json()) as XBotAuthorsTable; - if (!author_id) { - return createApiResponse('Missing required fields: authorId', 400); - } - const author = await addAuthor(this.orm, author_id, realname || undefined, username || undefined); - return createApiResponse({ - message: 'Successfully created author', - data: { author }, - }); - } - - if (endpoint === '/twitter/tweets/get') { - const tweetId = url.searchParams.get('tweetId'); - if (!tweetId) { - return createApiResponse('Missing tweetId parameter', 400); - } - const tweet = await getTweet(this.orm, tweetId); - return createApiResponse({ - message: 'Successfully retrieved tweet', - data: { tweet }, - }); - } - - if (endpoint === '/twitter/tweets/thread') { - const threadId = url.searchParams.get('threadId'); - if (!threadId) { - return createApiResponse('Missing threadId parameter', 400); - } - const tweets = await getThreadTweets(this.orm, parseInt(threadId)); - return createApiResponse({ - message: 'Successfully retrieved thread tweets', - data: { tweets }, - }); - } - - if (endpoint === '/twitter/tweets/author') { - const authorId = url.searchParams.get('authorId'); - if (!authorId) { - return createApiResponse('Missing authorId parameter', 400); - } - const tweets = await getAuthorTweets(this.orm, authorId); - return createApiResponse({ - message: 'Successfully retrieved author tweets', - data: { tweets }, - }); - } - - if (endpoint === '/twitter/tweets/add') { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const { author_id, tweet_id, tweet_body, thread_id, parent_tweet_id, is_bot_response } = (await request.json()) as XBotTweetsTable; - if (!author_id || !tweet_id || !tweet_body) { - return createApiResponse('Missing required fields: authorId, tweetId, tweetBody', 400); - } - const tweet = await addTweet( - this.orm, - author_id, - tweet_id, - tweet_body, - thread_id || undefined, - parent_tweet_id || undefined, - is_bot_response || undefined - ); - return createApiResponse({ - message: 'Successfully created tweet', - data: { tweet }, - }); - } - - if (endpoint === '/twitter/logs/get') { - const tweetId = url.searchParams.get('tweetId'); - if (!tweetId) { - return createApiResponse('Missing tweetId parameter', 400); - } - const logs = await getTweetLogs(this.orm, tweetId); - return createApiResponse({ - message: 'Successfully retrieved tweet logs', - data: { logs }, - }); - } - - if (endpoint === '/twitter/logs/add') { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const { tweet_id, tweet_status, log_message } = (await request.json()) as XBotLogsTable; - if (!tweet_id || !tweet_status) { - return createApiResponse('Missing required fields: tweetId, status', 400); - } - const log = await addLog(this.orm, tweet_id, tweet_status, log_message || undefined); - return createApiResponse({ - message: 'Successfully created log', - data: { log }, - }); - } - - return createUnsupportedEndpointResponse(endpoint, this.SUPPORTED_ENDPOINTS); } } diff --git a/src/database/handlers/twitter.ts b/src/database/handlers/twitter.ts new file mode 100644 index 0000000..644e98f --- /dev/null +++ b/src/database/handlers/twitter.ts @@ -0,0 +1,136 @@ +import { Handler } from './types'; +import { createApiResponse } from '../../utils/requests-responses'; +import { + getAuthor, + addAuthor, + getTweet, + getThreadTweets, + getAuthorTweets, + addTweet, + getTweetLogs, + addLog +} from '../helpers/twitter'; +import { XBotAuthorsTable, XBotTweetsTable, XBotLogsTable } from '../models'; + +export const handleTwitter: Handler = async ({ orm, request, url }) => { + const endpoint = url.pathname.split('/').pop(); + + switch (endpoint) { + case 'get': { + const authorId = url.searchParams.get('authorId'); + if (!authorId) { + return createApiResponse('Missing authorId parameter', 400); + } + const author = await getAuthor(orm, authorId); + return createApiResponse({ + message: 'Successfully retrieved author', + data: { author }, + }); + } + + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const { author_id, realname, username } = (await request.json()) as XBotAuthorsTable; + if (!author_id) { + return createApiResponse('Missing required fields: authorId', 400); + } + const author = await addAuthor(orm, author_id, realname || undefined, username || undefined); + return createApiResponse({ + message: 'Successfully created author', + data: { author }, + }); + } + + case 'tweet': { + const tweetId = url.searchParams.get('tweetId'); + if (!tweetId) { + return createApiResponse('Missing tweetId parameter', 400); + } + const tweet = await getTweet(orm, tweetId); + return createApiResponse({ + message: 'Successfully retrieved tweet', + data: { tweet }, + }); + } + + case 'thread': { + const threadId = url.searchParams.get('threadId'); + if (!threadId) { + return createApiResponse('Missing threadId parameter', 400); + } + const tweets = await getThreadTweets(orm, parseInt(threadId)); + return createApiResponse({ + message: 'Successfully retrieved thread tweets', + data: { tweets }, + }); + } + + case 'author-tweets': { + const authorId = url.searchParams.get('authorId'); + if (!authorId) { + return createApiResponse('Missing authorId parameter', 400); + } + const tweets = await getAuthorTweets(orm, authorId); + return createApiResponse({ + message: 'Successfully retrieved author tweets', + data: { tweets }, + }); + } + + case 'add-tweet': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const { author_id, tweet_id, tweet_body, thread_id, parent_tweet_id, is_bot_response } = + (await request.json()) as XBotTweetsTable; + if (!author_id || !tweet_id || !tweet_body) { + return createApiResponse('Missing required fields: authorId, tweetId, tweetBody', 400); + } + const tweet = await addTweet( + orm, + author_id, + tweet_id, + tweet_body, + thread_id || undefined, + parent_tweet_id || undefined, + is_bot_response || undefined + ); + return createApiResponse({ + message: 'Successfully created tweet', + data: { tweet }, + }); + } + + case 'logs': { + const tweetId = url.searchParams.get('tweetId'); + if (!tweetId) { + return createApiResponse('Missing tweetId parameter', 400); + } + const logs = await getTweetLogs(orm, tweetId); + return createApiResponse({ + message: 'Successfully retrieved tweet logs', + data: { logs }, + }); + } + + case 'add-log': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const { tweet_id, tweet_status, log_message } = (await request.json()) as XBotLogsTable; + if (!tweet_id || !tweet_status) { + return createApiResponse('Missing required fields: tweetId, status', 400); + } + const log = await addLog(orm, tweet_id, tweet_status, log_message || undefined); + return createApiResponse({ + message: 'Successfully created log', + data: { log }, + }); + } + + default: + return createApiResponse(`Unsupported twitter endpoint: ${endpoint}`, 404); + } +}; From 024ed7e96c6b317f9f483e88f815cae9de90cf6c Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 01:32:24 -0700 Subject: [PATCH 069/128] refactor: Reorganize twitter handler placement in database-do.ts --- src/database/database-do.ts | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 837f181..4e9e59e 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -212,7 +212,6 @@ export class DatabaseDO extends DurableObject { } } - // Pass off to crons handler if (endpoint.startsWith('/crons')) { const handler = getHandler(endpoint); @@ -238,24 +237,24 @@ export class DatabaseDO extends DurableObject { }); } } + + // Pass off to twitter handler + if (endpoint.startsWith('/twitter')) { + const handler = getHandler(endpoint); + if (handler) { + return handler({ + orm: this.orm, + env: this.env, + request, + url, + }); + } + } } catch (error) { console.error(`Database error: ${error instanceof Error ? error.message : String(error)}`); return createApiResponse(`Database error: ${error instanceof Error ? error.message : String(error)}`, 500); } - // Pass off to twitter handler - if (endpoint.startsWith('/twitter')) { - const handler = getHandler(endpoint); - if (handler) { - return handler({ - orm: this.orm, - env: this.env, - request, - url, - }); - } - } - return createUnsupportedEndpointResponse(endpoint, this.SUPPORTED_ENDPOINTS); } } From 5532a3e58219a4f97432a351960725320cb796a3 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 01:32:25 -0700 Subject: [PATCH 070/128] refactor: Simplify endpoint handler routing by removing redundant checks --- src/database/database-do.ts | 96 ++++--------------------------------- 1 file changed, 9 insertions(+), 87 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 4e9e59e..67e5eb3 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -162,93 +162,15 @@ export class DatabaseDO extends DurableObject { } try { - // pass off to handler - if (endpoint.startsWith('/conversations')) { - const handler = getHandler(endpoint); - if (handler) { - return handler({ - orm: this.orm, - env: this.env, - request, - url, - }); - } - } - - if (endpoint.startsWith('/crews')) { - const handler = getHandler(endpoint); - if (handler) { - return handler({ - orm: this.orm, - env: this.env, - request, - url, - }); - } - } - - if (endpoint.startsWith('/agents')) { - const handler = getHandler(endpoint); - if (handler) { - return handler({ - orm: this.orm, - env: this.env, - request, - url, - }); - } - } - - // Pass off to tasks handler - if (endpoint.startsWith('/tasks')) { - const handler = getHandler(endpoint); - if (handler) { - return handler({ - orm: this.orm, - env: this.env, - request, - url, - }); - } - } - - // Pass off to crons handler - if (endpoint.startsWith('/crons')) { - const handler = getHandler(endpoint); - if (handler) { - return handler({ - orm: this.orm, - env: this.env, - request, - url, - }); - } - } - - // Pass off to profiles handler - if (endpoint.startsWith('/profiles')) { - const handler = getHandler(endpoint); - if (handler) { - return handler({ - orm: this.orm, - env: this.env, - request, - url, - }); - } - } - - // Pass off to twitter handler - if (endpoint.startsWith('/twitter')) { - const handler = getHandler(endpoint); - if (handler) { - return handler({ - orm: this.orm, - env: this.env, - request, - url, - }); - } + // Get and execute handler if one exists for this endpoint + const handler = getHandler(endpoint); + if (handler) { + return handler({ + orm: this.orm, + env: this.env, + request, + url, + }); } } catch (error) { console.error(`Database error: ${error instanceof Error ? error.message : String(error)}`); From 63ca8eec0da7b9dbe8308666b5bdc15abc3449fe Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 01:42:53 -0700 Subject: [PATCH 071/128] feat: Add comprehensive endpoint management documentation --- docs/endpoint-management.md | 187 ++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 docs/endpoint-management.md diff --git a/docs/endpoint-management.md b/docs/endpoint-management.md new file mode 100644 index 0000000..bf36899 --- /dev/null +++ b/docs/endpoint-management.md @@ -0,0 +1,187 @@ +# API Endpoint Management + +This document describes our approach to managing API endpoints using TypeScript handlers with built-in documentation. + +## Overview + +Instead of maintaining a manual list of endpoints, we define endpoints alongside their handlers using TypeScript types and interfaces. This provides: + +- Self-documenting API endpoints +- Automatic endpoint validation +- Better developer experience +- Centralized endpoint documentation +- Helpful error messages with contextual endpoint suggestions + +## Implementation + +### 1. Handler Types + +```typescript +// src/database/handlers/types.ts +export interface HandlerEndpoint { + path: string; + methods: string[]; + description?: string; +} + +export interface HandlerDefinition { + baseRoute: string; + endpoints: HandlerEndpoint[]; + handler: Handler; +} + +export interface HandlerContext { + orm: D1Orm; + env: Env; + request: Request; + url: URL; +} + +export type Handler = (context: HandlerContext) => Promise; +``` + +### 2. Handler Implementation + +Example of a handler with endpoint definitions: + +```typescript +// src/database/handlers/profiles.ts +export const profilesHandler: HandlerDefinition = { + baseRoute: 'profiles', + endpoints: [ + { + path: '/profiles/role', + methods: ['GET'], + description: 'Get user role' + }, + { + path: '/profiles/get', + methods: ['GET'], + description: 'Get user profile' + }, + // ... more endpoints + ], + handler: async ({ orm, env, request, url }) => { + // Handler implementation + } +}; +``` + +### 3. Central Handler Registry + +```typescript +// src/database/handlers/index.ts +const handlerDefinitions: HandlerDefinition[] = [ + profilesHandler, + conversationsHandler, + // ... other handlers +]; + +export const getHandler = (path: string): Handler | undefined => { + const segment = path.split('/')[1]; + return handlers[segment]; +}; + +export const getSupportedEndpoints = (): string[] => { + return handlerDefinitions.flatMap(def => + def.endpoints.map(endpoint => endpoint.path) + ); +}; + +export const getEndpointDocumentation = () => { + return handlerDefinitions.map(def => ({ + baseRoute: def.baseRoute, + endpoints: def.endpoints + })); +}; +``` + +### 4. Error Handling + +```typescript +// src/utils/requests-responses.ts +export function createUnsupportedEndpointResponse( + endpoint: string, + documentation: ReturnType +): Response { + const baseRoute = endpoint.split('/')[1]; + let message = `Unsupported endpoint: ${endpoint}\n\n`; + + const routeDocs = documentation.find(d => d.baseRoute === baseRoute); + if (routeDocs) { + message += `Available endpoints for '${baseRoute}':\n`; + routeDocs.endpoints.forEach(e => { + message += ` ${e.path} [${e.methods.join(', ')}]`; + if (e.description) { + message += ` - ${e.description}`; + } + message += '\n'; + }); + } else { + message += 'Available routes:\n'; + documentation.forEach(d => { + message += ` /${d.baseRoute} endpoints:\n`; + d.endpoints.forEach(e => { + message += ` ${e.path} [${e.methods.join(', ')}]`; + if (e.description) { + message += ` - ${e.description}`; + } + message += '\n'; + }); + }); + } + + return createApiResponse(message, 404); +} +``` + +## Benefits + +1. **Self-Documentation**: Endpoints are defined alongside their implementation +2. **Type Safety**: TypeScript interfaces ensure consistent endpoint definitions +3. **Maintainability**: Single source of truth for endpoint definitions +4. **Developer Experience**: Helpful error messages with available endpoints +5. **Extensibility**: Easy to add new metadata fields to endpoints + +## Example Error Messages + +### Invalid Profile Endpoint +``` +Unsupported endpoint: /profiles/invalid + +Available endpoints for 'profiles': + /profiles/role [GET] - Get user role + /profiles/get [GET] - Get user profile + /profiles/create [POST] - Create new user profile + /profiles/update [PUT] - Update user profile + /profiles/delete [DELETE] - Delete user profile +``` + +### Unknown Route +``` +Unsupported endpoint: /unknown + +Available routes: + /profiles endpoints: + /profiles/role [GET] - Get user role + /profiles/get [GET] - Get user profile + /conversations endpoints: + /conversations/latest [GET] - Get latest conversation + /conversations/history [GET] - Get conversation history +``` + +## Implementation Steps + +1. Create the TypeScript interfaces in `types.ts` +2. Update existing handlers to use the new `HandlerDefinition` format +3. Create the central handler registry +4. Update error handling to use the new documentation system +5. Update the DatabaseDO class to use the new endpoint management + +## Future Enhancements + +- Add authentication requirements to endpoint definitions +- Include rate limiting information +- Add request/response schema documentation +- Generate OpenAPI/Swagger documentation +- Add versioning information to endpoints From 6414b126d1509883a3b81934fbbe1c90ef5fc37c Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 01:43:57 -0700 Subject: [PATCH 072/128] feat: add handlers to separate endpoints --- src/database/database-do.ts | 42 +-- src/database/handlers/agents.ts | 146 ++++---- src/database/handlers/conversations.ts | 96 ++--- src/database/handlers/crews.ts | 494 ++++++++++++------------- src/database/handlers/crons.ts | 175 +++++---- src/database/handlers/index.ts | 8 +- src/database/handlers/profiles.ts | 198 +++++----- src/database/handlers/tasks.ts | 201 +++++----- src/database/handlers/twitter.ts | 232 ++++++------ src/database/handlers/types.ts | 8 +- 10 files changed, 764 insertions(+), 836 deletions(-) diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 67e5eb3..518f7c4 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -4,47 +4,7 @@ import { AppConfig } from '../config'; import { createApiResponse, createUnsupportedEndpointResponse } from '../utils/requests-responses'; import { getHandler } from './handlers'; import { D1Orm } from 'd1-orm'; -import { - UserAgentsTable, - UserConversationsTable, - UserCrewExecutionsTable, - UserCrewExecutionStepsTable, - UserCrewsTable, - UserCronsTable, - UserProfilesTable, - UserTasksTable, - XBotAuthorsTable, - XBotLogsTable, - XBotTweetsTable, -} from './models'; -import { getAgents, createAgent, updateAgent, deleteAgent } from './helpers/agents'; -import { getAuthor, addAuthor, getTweet, getThreadTweets, getAuthorTweets, addTweet, getTweetLogs, addLog } from './helpers/twitter'; -import { - createCrew, - getCrew, - updateCrew, - deleteCrew, - getPublicCrews, - getCrewExecutions, - addCrewExecution, - getCrewsByProfile, - getExecutionSteps, - createExecutionStep, - deleteExecutionSteps, -} from './helpers/crews'; -import { getCronsByCrew, createCron, updateCronInput, toggleCronStatus, getEnabledCrons, getEnabledCronsDetailed } from './helpers/crons'; -import { - getUserRole, - getUserProfile, - createUserProfile, - updateUserProfile, - deleteUserProfile, - getAllUserProfiles, - updateUserProfileById, -} from './helpers/profiles'; -import { addConversation, getConversationHistory, getConversations, getLatestConversation } from './helpers/conversations'; -import { getTask, getTasks, createTask, updateTask, deleteTask, deleteTasks } from './helpers/tasks'; -import { validateSessionToken, validateSharedKeyAuth } from '../utils/auth-helper'; +import { validateSharedKeyAuth } from '../utils/auth-helper'; /** * Durable Object class for backend database calls diff --git a/src/database/handlers/agents.ts b/src/database/handlers/agents.ts index 88f0105..14a5878 100644 --- a/src/database/handlers/agents.ts +++ b/src/database/handlers/agents.ts @@ -1,89 +1,81 @@ import { Handler } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { validateSessionToken } from '../../utils/auth-helper'; -import { - getAgents, - createAgent, - updateAgent, - deleteAgent -} from '../helpers/agents'; +import { getAgents, createAgent, updateAgent, deleteAgent } from '../helpers/agents'; import { UserAgentsTable } from '../models'; export const handleAgents: Handler = async ({ orm, env, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + const endpoint = url.pathname.split('/').pop(); - switch (endpoint) { - case 'get': { - const crewId = url.searchParams.get('crewId'); - if (!crewId) { - return createApiResponse('Missing crewId parameter', 400); - } - const agents = await getAgents(orm, parseInt(crewId)); - return createApiResponse({ - message: 'Successfully retrieved agents', - data: { agents }, - }); - } + switch (endpoint) { + case 'get': { + const crewId = url.searchParams.get('crewId'); + if (!crewId) { + return createApiResponse('Missing crewId parameter', 400); + } + const agents = await getAgents(orm, parseInt(crewId)); + return createApiResponse({ + message: 'Successfully retrieved agents', + data: { agents }, + }); + } - case 'create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const agentData = (await request.json()) as Omit; - if ( - !agentData.profile_id || - !agentData.crew_id || - !agentData.agent_name || - !agentData.agent_role || - !agentData.agent_goal || - !agentData.agent_backstory - ) { - return createApiResponse( - 'Missing required fields: profile_id, crew_id, agent_name, agent_role, agent_goal, agent_backstory', - 400 - ); - } - const agent = await createAgent(orm, agentData); - return createApiResponse({ - message: 'Successfully created agent', - data: { agent }, - }); - } + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const agentData = (await request.json()) as Omit; + if ( + !agentData.profile_id || + !agentData.crew_id || + !agentData.agent_name || + !agentData.agent_role || + !agentData.agent_goal || + !agentData.agent_backstory + ) { + return createApiResponse('Missing required fields: profile_id, crew_id, agent_name, agent_role, agent_goal, agent_backstory', 400); + } + const agent = await createAgent(orm, agentData); + return createApiResponse({ + message: 'Successfully created agent', + data: { agent }, + }); + } - case 'update': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const agentId = url.searchParams.get('id'); - if (!agentId) { - return createApiResponse('Missing id parameter', 400); - } - const updates = (await request.json()) as Partial< - Omit - >; - const result = await updateAgent(orm, parseInt(agentId), updates); - return createApiResponse({ - message: 'Successfully updated agent', - data: { result }, - }); - } + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const agentId = url.searchParams.get('id'); + if (!agentId) { + return createApiResponse('Missing id parameter', 400); + } + const updates = (await request.json()) as Partial< + Omit + >; + const result = await updateAgent(orm, parseInt(agentId), updates); + return createApiResponse({ + message: 'Successfully updated agent', + data: { result }, + }); + } - case 'delete': { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const agentId = url.searchParams.get('id'); - if (!agentId) { - return createApiResponse('Missing id parameter', 400); - } - const result = await deleteAgent(orm, parseInt(agentId)); - return createApiResponse({ - message: 'Successfully deleted agent', - data: { result }, - }); - } + case 'delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const agentId = url.searchParams.get('id'); + if (!agentId) { + return createApiResponse('Missing id parameter', 400); + } + const result = await deleteAgent(orm, parseInt(agentId)); + return createApiResponse({ + message: 'Successfully deleted agent', + data: { result }, + }); + } - default: - return createApiResponse(`Unsupported agents endpoint: ${endpoint}`, 404); - } + default: + return createApiResponse(`Unsupported agents endpoint: ${endpoint}`, 404); + } }; diff --git a/src/database/handlers/conversations.ts b/src/database/handlers/conversations.ts index c8ce3e0..c6ddb21 100644 --- a/src/database/handlers/conversations.ts +++ b/src/database/handlers/conversations.ts @@ -4,57 +4,57 @@ import { getConversations, getLatestConversation, getConversationHistory, addCon import { UserConversationsTable } from '../models'; export const handleConversations: Handler = async ({ orm, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + const endpoint = url.pathname.split('/').pop(); - switch (endpoint) { - case 'conversations': - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const conversations = await getConversations(orm, address); - return createApiResponse({ - message: 'Successfully retrieved conversations', - data: conversations, - }); + switch (endpoint) { + case 'conversations': + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const conversations = await getConversations(orm, address); + return createApiResponse({ + message: 'Successfully retrieved conversations', + data: conversations, + }); - case 'latest': - const latestAddress = url.searchParams.get('address'); - if (!latestAddress) { - return createApiResponse('Missing address parameter', 400); - } - const conversation = await getLatestConversation(orm, latestAddress); - return createApiResponse({ - message: 'Successfully retrieved latest conversation', - data: conversation, - }); + case 'latest': + const latestAddress = url.searchParams.get('address'); + if (!latestAddress) { + return createApiResponse('Missing address parameter', 400); + } + const conversation = await getLatestConversation(orm, latestAddress); + return createApiResponse({ + message: 'Successfully retrieved latest conversation', + data: conversation, + }); - case 'history': - const conversationId = url.searchParams.get('id'); - if (!conversationId) { - return createApiResponse('Missing conversation ID parameter', 400); - } - const history = await getConversationHistory(orm, parseInt(conversationId)); - return createApiResponse({ - message: 'Successfully retrieved conversation history', - data: history, - }); + case 'history': + const conversationId = url.searchParams.get('id'); + if (!conversationId) { + return createApiResponse('Missing conversation ID parameter', 400); + } + const history = await getConversationHistory(orm, parseInt(conversationId)); + return createApiResponse({ + message: 'Successfully retrieved conversation history', + data: history, + }); - case 'create': - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const { profile_id, conversation_name } = (await request.json()) as UserConversationsTable; - if (!profile_id) { - return createApiResponse('Missing required field: address', 400); - } - const result = await addConversation(orm, profile_id, conversation_name ? conversation_name : 'new conversation'); - return createApiResponse({ - message: 'Successfully created conversation', - data: { result }, - }); + case 'create': + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const { profile_id, conversation_name } = (await request.json()) as UserConversationsTable; + if (!profile_id) { + return createApiResponse('Missing required field: address', 400); + } + const result = await addConversation(orm, profile_id, conversation_name ? conversation_name : 'new conversation'); + return createApiResponse({ + message: 'Successfully created conversation', + data: { result }, + }); - default: - return createApiResponse(`Unsupported conversations endpoint: ${endpoint}`, 404); - } + default: + return createApiResponse(`Unsupported conversations endpoint: ${endpoint}`, 404); + } }; diff --git a/src/database/handlers/crews.ts b/src/database/handlers/crews.ts index 096fb77..a0fb93b 100644 --- a/src/database/handlers/crews.ts +++ b/src/database/handlers/crews.ts @@ -1,255 +1,255 @@ import { Handler } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { validateSessionToken } from '../../utils/auth-helper'; -import { - getCrewsByProfile, - getPublicCrews, - getCrew, - createCrew, - updateCrew, - deleteCrew, - getCrewExecutions, - addCrewExecution, - getExecutionSteps, - createExecutionStep, - deleteExecutionSteps +import { + getCrewsByProfile, + getPublicCrews, + getCrew, + createCrew, + updateCrew, + deleteCrew, + getCrewExecutions, + addCrewExecution, + getExecutionSteps, + createExecutionStep, + deleteExecutionSteps, } from '../helpers/crews'; import { UserCrewsTable, UserCrewExecutionStepsTable } from '../models'; export const handleCrews: Handler = async ({ orm, env, request, url }) => { - const endpoint = url.pathname.split('/').pop(); - - switch (endpoint) { - case 'profile': { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - - // Get the session token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader) { - return createApiResponse('Missing authorization header', 401); - } - - // Extract token from Bearer format - const token = authHeader.replace('Bearer ', ''); - - // Verify the token matches the requested address - const tokenAddress = await validateSessionToken(env, token); - if (!tokenAddress.success || tokenAddress.address !== address) { - return createApiResponse('Unauthorized access', 403); - } - - const crews = await getCrewsByProfile(orm, address); - return createApiResponse({ - message: 'Successfully retrieved profile crews', - data: { crews }, - }); - } - - case 'public': { - const crews = await getPublicCrews(orm); - return createApiResponse({ - message: 'Successfully retrieved public crews', - data: { crews }, - }); - } - - case 'get': { - const crewId = url.searchParams.get('id'); - if (!crewId) { - return createApiResponse('Missing id parameter', 400); - } - const crew = await getCrew(orm, parseInt(crewId)); - return createApiResponse({ - message: 'Successfully retrieved crew', - data: { crew }, - }); - } - - case 'create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const crewData = (await request.json()) as Omit; - if (!crewData.profile_id || !crewData.crew_name) { - return createApiResponse('Missing required fields: profile_id, crew_name', 400); - } - const crew = await createCrew(orm, crewData); - return createApiResponse({ - message: 'Successfully created crew', - data: { crew }, - }); - } - - case 'update': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const crewId = url.searchParams.get('id'); - if (!crewId) { - return createApiResponse('Missing id parameter', 400); - } - const updates = (await request.json()) as Partial>; - const result = await updateCrew(orm, parseInt(crewId), updates); - return createApiResponse({ - message: 'Successfully updated crew', - data: { result }, - }); - } - - case 'delete': { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const crewId = url.searchParams.get('id'); - if (!crewId) { - return createApiResponse('Missing id parameter', 400); - } - const result = await deleteCrew(orm, parseInt(crewId)); - return createApiResponse({ - message: 'Successfully deleted crew', - data: { result }, - }); - } - - case 'executions': { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const executions = await getCrewExecutions(orm, address); - return createApiResponse({ - message: 'Successfully retrieved crew executions', - data: { executions }, - }); - } - - case 'executions/add': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - - type AddCrewExecutionRequest = { - address: string; - crewId: number; - conversationId: number; - input: string; - }; - - const body: AddCrewExecutionRequest = await request.json(); - const { address, crewId, conversationId, input } = body; - - if (!address || !crewId || !conversationId || !input) { - return createApiResponse('Missing required parameters: address, crewId, conversationId, input', 400); - } - - const execution = await addCrewExecution(orm, address, crewId, conversationId, input); - return createApiResponse({ - message: 'Successfully created crew execution', - data: { execution }, - }); - } - - case 'steps/get': { - const executionId = url.searchParams.get('executionId'); - if (!executionId) { - return createApiResponse('Missing executionId parameter', 400); - } - - // Get the session token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader) { - return createApiResponse('Missing authorization header', 401); - } - - // Extract token from Bearer format - const token = authHeader.replace('Bearer ', ''); - - // Verify the token matches the requested address - const tokenAddress = await validateSessionToken(env, token); - if (!tokenAddress.success) { - return createApiResponse('Unauthorized access', 403); - } - - const steps = await getExecutionSteps(orm, parseInt(executionId)); - return createApiResponse({ - message: 'Successfully retrieved execution steps', - data: { steps }, - }); - } - - case 'steps/create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - - // Get the session token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader) { - return createApiResponse('Missing authorization header', 401); - } - - // Extract token from Bearer format - const token = authHeader.replace('Bearer ', ''); - - // Verify the token - const tokenAddress = await validateSessionToken(env, token); - if (!tokenAddress.success) { - return createApiResponse('Unauthorized access', 403); - } - - const stepData = (await request.json()) as UserCrewExecutionStepsTable; - if (!stepData.profile_id || !stepData.crew_id || !stepData.execution_id || !stepData.step_type || !stepData.step_data) { - return createApiResponse('Missing required fields: profile_id, crew_id, execution_id, step_type, step_data', 400); - } - - // Verify the profile_id matches the token address - if (stepData.profile_id !== tokenAddress.address) { - return createApiResponse('Unauthorized: profile_id does not match token', 403); - } - - const step = await createExecutionStep(orm, stepData); - return createApiResponse({ - message: 'Successfully created execution step', - data: { step }, - }); - } - - case 'steps/delete': { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - - const executionId = url.searchParams.get('executionId'); - if (!executionId) { - return createApiResponse('Missing executionId parameter', 400); - } - - // Get the session token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader) { - return createApiResponse('Missing authorization header', 401); - } - - // Extract token from Bearer format - const token = authHeader.replace('Bearer ', ''); - - // Verify the token - const tokenAddress = await validateSessionToken(env, token); - if (!tokenAddress.success) { - return createApiResponse('Unauthorized access', 403); - } - - const result = await deleteExecutionSteps(orm, parseInt(executionId)); - return createApiResponse({ - message: 'Successfully deleted execution steps', - data: { result }, - }); - } - - default: - return createApiResponse(`Unsupported crews endpoint: ${endpoint}`, 404); - } + const endpoint = url.pathname.split('/').pop(); + + switch (endpoint) { + case 'profile': { + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + + // Get the session token from Authorization header + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + + // Extract token from Bearer format + const token = authHeader.replace('Bearer ', ''); + + // Verify the token matches the requested address + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success || tokenAddress.address !== address) { + return createApiResponse('Unauthorized access', 403); + } + + const crews = await getCrewsByProfile(orm, address); + return createApiResponse({ + message: 'Successfully retrieved profile crews', + data: { crews }, + }); + } + + case 'public': { + const crews = await getPublicCrews(orm); + return createApiResponse({ + message: 'Successfully retrieved public crews', + data: { crews }, + }); + } + + case 'get': { + const crewId = url.searchParams.get('id'); + if (!crewId) { + return createApiResponse('Missing id parameter', 400); + } + const crew = await getCrew(orm, parseInt(crewId)); + return createApiResponse({ + message: 'Successfully retrieved crew', + data: { crew }, + }); + } + + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const crewData = (await request.json()) as Omit; + if (!crewData.profile_id || !crewData.crew_name) { + return createApiResponse('Missing required fields: profile_id, crew_name', 400); + } + const crew = await createCrew(orm, crewData); + return createApiResponse({ + message: 'Successfully created crew', + data: { crew }, + }); + } + + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const crewId = url.searchParams.get('id'); + if (!crewId) { + return createApiResponse('Missing id parameter', 400); + } + const updates = (await request.json()) as Partial>; + const result = await updateCrew(orm, parseInt(crewId), updates); + return createApiResponse({ + message: 'Successfully updated crew', + data: { result }, + }); + } + + case 'delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const crewId = url.searchParams.get('id'); + if (!crewId) { + return createApiResponse('Missing id parameter', 400); + } + const result = await deleteCrew(orm, parseInt(crewId)); + return createApiResponse({ + message: 'Successfully deleted crew', + data: { result }, + }); + } + + case 'executions': { + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const executions = await getCrewExecutions(orm, address); + return createApiResponse({ + message: 'Successfully retrieved crew executions', + data: { executions }, + }); + } + + case 'executions/add': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + + type AddCrewExecutionRequest = { + address: string; + crewId: number; + conversationId: number; + input: string; + }; + + const body: AddCrewExecutionRequest = await request.json(); + const { address, crewId, conversationId, input } = body; + + if (!address || !crewId || !conversationId || !input) { + return createApiResponse('Missing required parameters: address, crewId, conversationId, input', 400); + } + + const execution = await addCrewExecution(orm, address, crewId, conversationId, input); + return createApiResponse({ + message: 'Successfully created crew execution', + data: { execution }, + }); + } + + case 'steps/get': { + const executionId = url.searchParams.get('executionId'); + if (!executionId) { + return createApiResponse('Missing executionId parameter', 400); + } + + // Get the session token from Authorization header + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + + // Extract token from Bearer format + const token = authHeader.replace('Bearer ', ''); + + // Verify the token matches the requested address + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success) { + return createApiResponse('Unauthorized access', 403); + } + + const steps = await getExecutionSteps(orm, parseInt(executionId)); + return createApiResponse({ + message: 'Successfully retrieved execution steps', + data: { steps }, + }); + } + + case 'steps/create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + + // Get the session token from Authorization header + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + + // Extract token from Bearer format + const token = authHeader.replace('Bearer ', ''); + + // Verify the token + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success) { + return createApiResponse('Unauthorized access', 403); + } + + const stepData = (await request.json()) as UserCrewExecutionStepsTable; + if (!stepData.profile_id || !stepData.crew_id || !stepData.execution_id || !stepData.step_type || !stepData.step_data) { + return createApiResponse('Missing required fields: profile_id, crew_id, execution_id, step_type, step_data', 400); + } + + // Verify the profile_id matches the token address + if (stepData.profile_id !== tokenAddress.address) { + return createApiResponse('Unauthorized: profile_id does not match token', 403); + } + + const step = await createExecutionStep(orm, stepData); + return createApiResponse({ + message: 'Successfully created execution step', + data: { step }, + }); + } + + case 'steps/delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + + const executionId = url.searchParams.get('executionId'); + if (!executionId) { + return createApiResponse('Missing executionId parameter', 400); + } + + // Get the session token from Authorization header + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + + // Extract token from Bearer format + const token = authHeader.replace('Bearer ', ''); + + // Verify the token + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success) { + return createApiResponse('Unauthorized access', 403); + } + + const result = await deleteExecutionSteps(orm, parseInt(executionId)); + return createApiResponse({ + message: 'Successfully deleted execution steps', + data: { result }, + }); + } + + default: + return createApiResponse(`Unsupported crews endpoint: ${endpoint}`, 404); + } }; diff --git a/src/database/handlers/crons.ts b/src/database/handlers/crons.ts index 1099040..a65d5e1 100644 --- a/src/database/handlers/crons.ts +++ b/src/database/handlers/crons.ts @@ -1,105 +1,98 @@ import { Handler } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { validateSessionToken } from '../../utils/auth-helper'; -import { - getCronsByCrew, - createCron, - updateCronInput, - toggleCronStatus, - getEnabledCrons, - getEnabledCronsDetailed -} from '../helpers/crons'; +import { getCronsByCrew, createCron, updateCronInput, toggleCronStatus, getEnabledCrons, getEnabledCronsDetailed } from '../helpers/crons'; import { UserCronsTable } from '../models'; export const handleCrons: Handler = async ({ orm, env, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + const endpoint = url.pathname.split('/').pop(); - switch (endpoint) { - case 'enabled': { - const crons = await getEnabledCrons(orm); - return createApiResponse({ - message: 'Successfully retrieved enabled crons', - data: { crons }, - }); - } + switch (endpoint) { + case 'enabled': { + const crons = await getEnabledCrons(orm); + return createApiResponse({ + message: 'Successfully retrieved enabled crons', + data: { crons }, + }); + } - case 'enabled-detailed': { - const crons = await getEnabledCronsDetailed(orm); - return createApiResponse({ - message: 'Successfully retrieved detailed cron information', - data: { crons }, - }); - } + case 'enabled-detailed': { + const crons = await getEnabledCronsDetailed(orm); + return createApiResponse({ + message: 'Successfully retrieved detailed cron information', + data: { crons }, + }); + } - case 'get': { - const crewId = url.searchParams.get('crewId'); - if (!crewId) { - return createApiResponse('Missing crewId parameter', 400); - } - const crons = await getCronsByCrew(orm, parseInt(crewId)); - return createApiResponse({ - message: 'Successfully retrieved crons for crew', - data: { crons }, - }); - } + case 'get': { + const crewId = url.searchParams.get('crewId'); + if (!crewId) { + return createApiResponse('Missing crewId parameter', 400); + } + const crons = await getCronsByCrew(orm, parseInt(crewId)); + return createApiResponse({ + message: 'Successfully retrieved crons for crew', + data: { crons }, + }); + } - case 'create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const cronData = (await request.json()) as UserCronsTable; - if (!cronData.profile_id || !cronData.crew_id || cronData.cron_enabled === undefined) { - return createApiResponse('Missing required fields: profile_id, crew_id, cron_enabled', 400); - } - // Set defaults if not provided - cronData.cron_interval = cronData.cron_interval || '0 * * * *'; // Default to hourly - cronData.cron_input = cronData.cron_input || ''; - const cron = await createCron(orm, cronData); - return createApiResponse({ - message: 'Successfully created cron', - data: { cron }, - }); - } + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const cronData = (await request.json()) as UserCronsTable; + if (!cronData.profile_id || !cronData.crew_id || cronData.cron_enabled === undefined) { + return createApiResponse('Missing required fields: profile_id, crew_id, cron_enabled', 400); + } + // Set defaults if not provided + cronData.cron_interval = cronData.cron_interval || '0 * * * *'; // Default to hourly + cronData.cron_input = cronData.cron_input || ''; + const cron = await createCron(orm, cronData); + return createApiResponse({ + message: 'Successfully created cron', + data: { cron }, + }); + } - case 'update': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const cronId = url.searchParams.get('id'); - if (!cronId) { - return createApiResponse('Missing id parameter', 400); - } - const { cron_input } = (await request.json()) as UserCronsTable; - if (cron_input === undefined) { - return createApiResponse('Missing cron_input in request body', 400); - } - const result = await updateCronInput(orm, parseInt(cronId), cron_input); - return createApiResponse({ - message: 'Successfully updated cron input', - data: { result }, - }); - } + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const cronId = url.searchParams.get('id'); + if (!cronId) { + return createApiResponse('Missing id parameter', 400); + } + const { cron_input } = (await request.json()) as UserCronsTable; + if (cron_input === undefined) { + return createApiResponse('Missing cron_input in request body', 400); + } + const result = await updateCronInput(orm, parseInt(cronId), cron_input); + return createApiResponse({ + message: 'Successfully updated cron input', + data: { result }, + }); + } - case 'toggle': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const cronId = url.searchParams.get('id'); - if (!cronId) { - return createApiResponse('Missing id parameter', 400); - } - const { cron_enabled } = (await request.json()) as UserCronsTable; - if (cron_enabled === undefined) { - return createApiResponse('Missing cron_enabled in request body', 400); - } - const result = await toggleCronStatus(orm, parseInt(cronId), cron_enabled ? 1 : 0); - return createApiResponse({ - message: 'Successfully toggled cron status', - data: { result }, - }); - } + case 'toggle': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const cronId = url.searchParams.get('id'); + if (!cronId) { + return createApiResponse('Missing id parameter', 400); + } + const { cron_enabled } = (await request.json()) as UserCronsTable; + if (cron_enabled === undefined) { + return createApiResponse('Missing cron_enabled in request body', 400); + } + const result = await toggleCronStatus(orm, parseInt(cronId), cron_enabled ? 1 : 0); + return createApiResponse({ + message: 'Successfully toggled cron status', + data: { result }, + }); + } - default: - return createApiResponse(`Unsupported crons endpoint: ${endpoint}`, 404); - } + default: + return createApiResponse(`Unsupported crons endpoint: ${endpoint}`, 404); + } }; diff --git a/src/database/handlers/index.ts b/src/database/handlers/index.ts index 403a2ee..d0639e2 100644 --- a/src/database/handlers/index.ts +++ b/src/database/handlers/index.ts @@ -2,11 +2,11 @@ import { Handler } from './types'; import { handleConversations } from './conversations'; const handlers: Record = { - conversations: handleConversations, - // Add more handlers as needed + conversations: handleConversations, + // Add more handlers as needed }; export const getHandler = (path: string): Handler | undefined => { - const segment = path.split('/')[1]; - return handlers[segment]; + const segment = path.split('/')[1]; + return handlers[segment]; }; diff --git a/src/database/handlers/profiles.ts b/src/database/handlers/profiles.ts index 1b75bf5..f9f77d6 100644 --- a/src/database/handlers/profiles.ts +++ b/src/database/handlers/profiles.ts @@ -1,115 +1,115 @@ import { Handler } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { - getUserRole, - getUserProfile, - createUserProfile, - updateUserProfile, - deleteUserProfile, - getAllUserProfiles, - updateUserProfileById + getUserRole, + getUserProfile, + createUserProfile, + updateUserProfile, + deleteUserProfile, + getAllUserProfiles, + updateUserProfileById, } from '../helpers/profiles'; import { UserProfilesTable } from '../models'; export const handleProfiles: Handler = async ({ orm, env, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + const endpoint = url.pathname.split('/').pop(); - switch (endpoint) { - case 'role': { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const role = await getUserRole(orm, address); - return createApiResponse({ - message: 'Successfully retrieved user role', - data: { role }, - }); - } + switch (endpoint) { + case 'role': { + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const role = await getUserRole(orm, address); + return createApiResponse({ + message: 'Successfully retrieved user role', + data: { role }, + }); + } - case 'get': { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const profile = await getUserProfile(orm, address); - return createApiResponse({ - message: 'Successfully retrieved user profile', - data: { profile }, - }); - } + case 'get': { + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const profile = await getUserProfile(orm, address); + return createApiResponse({ + message: 'Successfully retrieved user profile', + data: { profile }, + }); + } - case 'create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const profileData = (await request.json()) as UserProfilesTable; - if (!profileData.stx_address || !profileData.user_role) { - return createApiResponse('Missing required fields: stx_address, user_role', 400); - } - const profile = await createUserProfile(orm, profileData); - return createApiResponse({ - message: 'Successfully created user profile', - data: { profile }, - }); - } + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const profileData = (await request.json()) as UserProfilesTable; + if (!profileData.stx_address || !profileData.user_role) { + return createApiResponse('Missing required fields: stx_address, user_role', 400); + } + const profile = await createUserProfile(orm, profileData); + return createApiResponse({ + message: 'Successfully created user profile', + data: { profile }, + }); + } - case 'update': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const profileData = (await request.json()) as UserProfilesTable; - const result = await updateUserProfile(orm, address, profileData); - return createApiResponse({ - message: 'Successfully updated user profile', - data: { result }, - }); - } + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const profileData = (await request.json()) as UserProfilesTable; + const result = await updateUserProfile(orm, address, profileData); + return createApiResponse({ + message: 'Successfully updated user profile', + data: { result }, + }); + } - case 'delete': { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const result = await deleteUserProfile(orm, address); - return createApiResponse({ - message: 'Successfully deleted user profile', - data: { result }, - }); - } + case 'delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const result = await deleteUserProfile(orm, address); + return createApiResponse({ + message: 'Successfully deleted user profile', + data: { result }, + }); + } - case 'list': { - const profiles = await getAllUserProfiles(orm); - return createApiResponse({ - message: 'Successfully retrieved all user profiles', - data: { profiles }, - }); - } + case 'list': { + const profiles = await getAllUserProfiles(orm); + return createApiResponse({ + message: 'Successfully retrieved all user profiles', + data: { profiles }, + }); + } - case 'admin-update': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const userId = url.searchParams.get('userId'); - if (!userId) { - return createApiResponse('Missing userId parameter', 400); - } - const updates = (await request.json()) as UserProfilesTable; - const result = await updateUserProfileById(orm, parseInt(userId), updates); - return createApiResponse({ - message: 'Successfully updated user profile', - data: { result }, - }); - } + case 'admin-update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const userId = url.searchParams.get('userId'); + if (!userId) { + return createApiResponse('Missing userId parameter', 400); + } + const updates = (await request.json()) as UserProfilesTable; + const result = await updateUserProfileById(orm, parseInt(userId), updates); + return createApiResponse({ + message: 'Successfully updated user profile', + data: { result }, + }); + } - default: - return createApiResponse(`Unsupported profiles endpoint: ${endpoint}`, 404); - } + default: + return createApiResponse(`Unsupported profiles endpoint: ${endpoint}`, 404); + } }; diff --git a/src/database/handlers/tasks.ts b/src/database/handlers/tasks.ts index bf61923..10aba82 100644 --- a/src/database/handlers/tasks.ts +++ b/src/database/handlers/tasks.ts @@ -1,118 +1,111 @@ import { Handler } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { validateSessionToken } from '../../utils/auth-helper'; -import { - getTask, - getTasks, - createTask, - updateTask, - deleteTask, - deleteTasks -} from '../helpers/tasks'; +import { getTask, getTasks, createTask, updateTask, deleteTask, deleteTasks } from '../helpers/tasks'; import { UserTasksTable } from '../models'; export const handleTasks: Handler = async ({ orm, env, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + const endpoint = url.pathname.split('/').pop(); - switch (endpoint) { - case 'get': { - const taskId = url.searchParams.get('id'); - if (!taskId) { - return createApiResponse('Missing id parameter', 400); - } - const task = await getTask(orm, parseInt(taskId)); - return createApiResponse({ - message: 'Successfully retrieved task', - data: { task }, - }); - } + switch (endpoint) { + case 'get': { + const taskId = url.searchParams.get('id'); + if (!taskId) { + return createApiResponse('Missing id parameter', 400); + } + const task = await getTask(orm, parseInt(taskId)); + return createApiResponse({ + message: 'Successfully retrieved task', + data: { task }, + }); + } - case 'list': { - const agentId = url.searchParams.get('agentId'); - if (!agentId) { - return createApiResponse('Missing agentId parameter', 400); - } - const tasks = await getTasks(orm, parseInt(agentId)); - return createApiResponse({ - message: 'Successfully retrieved tasks', - data: { tasks }, - }); - } + case 'list': { + const agentId = url.searchParams.get('agentId'); + if (!agentId) { + return createApiResponse('Missing agentId parameter', 400); + } + const tasks = await getTasks(orm, parseInt(agentId)); + return createApiResponse({ + message: 'Successfully retrieved tasks', + data: { tasks }, + }); + } - case 'create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const taskData = (await request.json()) as UserTasksTable; - if ( - !taskData.profile_id || - !taskData.crew_id || - !taskData.agent_id || - !taskData.task_name || - !taskData.task_description || - !taskData.task_expected_output - ) { - return createApiResponse( - 'Missing required fields: profile_id, crew_id, agent_id, task_name, task_description, task_expected_output', - 400 - ); - } - const task = await createTask(orm, taskData); - return createApiResponse({ - message: 'Successfully created task', - data: { task }, - }); - } + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const taskData = (await request.json()) as UserTasksTable; + if ( + !taskData.profile_id || + !taskData.crew_id || + !taskData.agent_id || + !taskData.task_name || + !taskData.task_description || + !taskData.task_expected_output + ) { + return createApiResponse( + 'Missing required fields: profile_id, crew_id, agent_id, task_name, task_description, task_expected_output', + 400 + ); + } + const task = await createTask(orm, taskData); + return createApiResponse({ + message: 'Successfully created task', + data: { task }, + }); + } - case 'update': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const taskId = url.searchParams.get('id'); - if (!taskId) { - return createApiResponse('Missing id parameter', 400); - } - const updates = (await request.json()) as Partial< - Omit - >; - const result = await updateTask(orm, parseInt(taskId), updates); - return createApiResponse({ - message: 'Successfully updated task', - data: { result }, - }); - } + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const taskId = url.searchParams.get('id'); + if (!taskId) { + return createApiResponse('Missing id parameter', 400); + } + const updates = (await request.json()) as Partial< + Omit + >; + const result = await updateTask(orm, parseInt(taskId), updates); + return createApiResponse({ + message: 'Successfully updated task', + data: { result }, + }); + } - case 'delete': { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const taskId = url.searchParams.get('id'); - if (!taskId) { - return createApiResponse('Missing id parameter', 400); - } - const result = await deleteTask(orm, parseInt(taskId)); - return createApiResponse({ - message: 'Successfully deleted task', - data: { result }, - }); - } + case 'delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const taskId = url.searchParams.get('id'); + if (!taskId) { + return createApiResponse('Missing id parameter', 400); + } + const result = await deleteTask(orm, parseInt(taskId)); + return createApiResponse({ + message: 'Successfully deleted task', + data: { result }, + }); + } - case 'delete-all': { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const agentId = url.searchParams.get('agentId'); - if (!agentId) { - return createApiResponse('Missing agentId parameter', 400); - } - const result = await deleteTasks(orm, parseInt(agentId)); - return createApiResponse({ - message: 'Successfully deleted all tasks for agent', - data: { result }, - }); - } + case 'delete-all': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const agentId = url.searchParams.get('agentId'); + if (!agentId) { + return createApiResponse('Missing agentId parameter', 400); + } + const result = await deleteTasks(orm, parseInt(agentId)); + return createApiResponse({ + message: 'Successfully deleted all tasks for agent', + data: { result }, + }); + } - default: - return createApiResponse(`Unsupported tasks endpoint: ${endpoint}`, 404); - } + default: + return createApiResponse(`Unsupported tasks endpoint: ${endpoint}`, 404); + } }; diff --git a/src/database/handlers/twitter.ts b/src/database/handlers/twitter.ts index 644e98f..ba3be05 100644 --- a/src/database/handlers/twitter.ts +++ b/src/database/handlers/twitter.ts @@ -1,136 +1,126 @@ import { Handler } from './types'; import { createApiResponse } from '../../utils/requests-responses'; -import { - getAuthor, - addAuthor, - getTweet, - getThreadTweets, - getAuthorTweets, - addTweet, - getTweetLogs, - addLog -} from '../helpers/twitter'; +import { getAuthor, addAuthor, getTweet, getThreadTweets, getAuthorTweets, addTweet, getTweetLogs, addLog } from '../helpers/twitter'; import { XBotAuthorsTable, XBotTweetsTable, XBotLogsTable } from '../models'; export const handleTwitter: Handler = async ({ orm, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + const endpoint = url.pathname.split('/').pop(); - switch (endpoint) { - case 'get': { - const authorId = url.searchParams.get('authorId'); - if (!authorId) { - return createApiResponse('Missing authorId parameter', 400); - } - const author = await getAuthor(orm, authorId); - return createApiResponse({ - message: 'Successfully retrieved author', - data: { author }, - }); - } + switch (endpoint) { + case 'get': { + const authorId = url.searchParams.get('authorId'); + if (!authorId) { + return createApiResponse('Missing authorId parameter', 400); + } + const author = await getAuthor(orm, authorId); + return createApiResponse({ + message: 'Successfully retrieved author', + data: { author }, + }); + } - case 'create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const { author_id, realname, username } = (await request.json()) as XBotAuthorsTable; - if (!author_id) { - return createApiResponse('Missing required fields: authorId', 400); - } - const author = await addAuthor(orm, author_id, realname || undefined, username || undefined); - return createApiResponse({ - message: 'Successfully created author', - data: { author }, - }); - } + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const { author_id, realname, username } = (await request.json()) as XBotAuthorsTable; + if (!author_id) { + return createApiResponse('Missing required fields: authorId', 400); + } + const author = await addAuthor(orm, author_id, realname || undefined, username || undefined); + return createApiResponse({ + message: 'Successfully created author', + data: { author }, + }); + } - case 'tweet': { - const tweetId = url.searchParams.get('tweetId'); - if (!tweetId) { - return createApiResponse('Missing tweetId parameter', 400); - } - const tweet = await getTweet(orm, tweetId); - return createApiResponse({ - message: 'Successfully retrieved tweet', - data: { tweet }, - }); - } + case 'tweet': { + const tweetId = url.searchParams.get('tweetId'); + if (!tweetId) { + return createApiResponse('Missing tweetId parameter', 400); + } + const tweet = await getTweet(orm, tweetId); + return createApiResponse({ + message: 'Successfully retrieved tweet', + data: { tweet }, + }); + } - case 'thread': { - const threadId = url.searchParams.get('threadId'); - if (!threadId) { - return createApiResponse('Missing threadId parameter', 400); - } - const tweets = await getThreadTweets(orm, parseInt(threadId)); - return createApiResponse({ - message: 'Successfully retrieved thread tweets', - data: { tweets }, - }); - } + case 'thread': { + const threadId = url.searchParams.get('threadId'); + if (!threadId) { + return createApiResponse('Missing threadId parameter', 400); + } + const tweets = await getThreadTweets(orm, parseInt(threadId)); + return createApiResponse({ + message: 'Successfully retrieved thread tweets', + data: { tweets }, + }); + } - case 'author-tweets': { - const authorId = url.searchParams.get('authorId'); - if (!authorId) { - return createApiResponse('Missing authorId parameter', 400); - } - const tweets = await getAuthorTweets(orm, authorId); - return createApiResponse({ - message: 'Successfully retrieved author tweets', - data: { tweets }, - }); - } + case 'author-tweets': { + const authorId = url.searchParams.get('authorId'); + if (!authorId) { + return createApiResponse('Missing authorId parameter', 400); + } + const tweets = await getAuthorTweets(orm, authorId); + return createApiResponse({ + message: 'Successfully retrieved author tweets', + data: { tweets }, + }); + } - case 'add-tweet': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const { author_id, tweet_id, tweet_body, thread_id, parent_tweet_id, is_bot_response } = - (await request.json()) as XBotTweetsTable; - if (!author_id || !tweet_id || !tweet_body) { - return createApiResponse('Missing required fields: authorId, tweetId, tweetBody', 400); - } - const tweet = await addTweet( - orm, - author_id, - tweet_id, - tweet_body, - thread_id || undefined, - parent_tweet_id || undefined, - is_bot_response || undefined - ); - return createApiResponse({ - message: 'Successfully created tweet', - data: { tweet }, - }); - } + case 'add-tweet': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const { author_id, tweet_id, tweet_body, thread_id, parent_tweet_id, is_bot_response } = (await request.json()) as XBotTweetsTable; + if (!author_id || !tweet_id || !tweet_body) { + return createApiResponse('Missing required fields: authorId, tweetId, tweetBody', 400); + } + const tweet = await addTweet( + orm, + author_id, + tweet_id, + tweet_body, + thread_id || undefined, + parent_tweet_id || undefined, + is_bot_response || undefined + ); + return createApiResponse({ + message: 'Successfully created tweet', + data: { tweet }, + }); + } - case 'logs': { - const tweetId = url.searchParams.get('tweetId'); - if (!tweetId) { - return createApiResponse('Missing tweetId parameter', 400); - } - const logs = await getTweetLogs(orm, tweetId); - return createApiResponse({ - message: 'Successfully retrieved tweet logs', - data: { logs }, - }); - } + case 'logs': { + const tweetId = url.searchParams.get('tweetId'); + if (!tweetId) { + return createApiResponse('Missing tweetId parameter', 400); + } + const logs = await getTweetLogs(orm, tweetId); + return createApiResponse({ + message: 'Successfully retrieved tweet logs', + data: { logs }, + }); + } - case 'add-log': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const { tweet_id, tweet_status, log_message } = (await request.json()) as XBotLogsTable; - if (!tweet_id || !tweet_status) { - return createApiResponse('Missing required fields: tweetId, status', 400); - } - const log = await addLog(orm, tweet_id, tweet_status, log_message || undefined); - return createApiResponse({ - message: 'Successfully created log', - data: { log }, - }); - } + case 'add-log': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const { tweet_id, tweet_status, log_message } = (await request.json()) as XBotLogsTable; + if (!tweet_id || !tweet_status) { + return createApiResponse('Missing required fields: tweetId, status', 400); + } + const log = await addLog(orm, tweet_id, tweet_status, log_message || undefined); + return createApiResponse({ + message: 'Successfully created log', + data: { log }, + }); + } - default: - return createApiResponse(`Unsupported twitter endpoint: ${endpoint}`, 404); - } + default: + return createApiResponse(`Unsupported twitter endpoint: ${endpoint}`, 404); + } }; diff --git a/src/database/handlers/types.ts b/src/database/handlers/types.ts index abbd265..66a7ef4 100644 --- a/src/database/handlers/types.ts +++ b/src/database/handlers/types.ts @@ -2,10 +2,10 @@ import { D1Orm } from 'd1-orm'; import { Env } from '../../../worker-configuration'; export interface HandlerContext { - orm: D1Orm; - env: Env; - request: Request; - url: URL; + orm: D1Orm; + env: Env; + request: Request; + url: URL; } export type Handler = (context: HandlerContext) => Promise; From eb596a7f1b39fe93f50a17505e0856702d7dcdbc Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 01:44:39 -0700 Subject: [PATCH 073/128] chore: cleanup and formatting --- ...int_structure.md => endpoint-structure.md} | 0 src/database/helpers/agents.ts | 196 +++--- src/database/helpers/conversations.ts | 642 +++++++++--------- src/database/helpers/crews.ts | 477 ++++++------- src/database/helpers/crons.ts | 366 +++++----- src/database/helpers/index.ts | 14 +- src/database/helpers/profiles.ts | 276 ++++---- src/database/helpers/tasks.ts | 232 +++---- src/database/helpers/twitter.ts | 190 +++--- 9 files changed, 1186 insertions(+), 1207 deletions(-) rename docs/{endpoint_structure.md => endpoint-structure.md} (100%) diff --git a/docs/endpoint_structure.md b/docs/endpoint-structure.md similarity index 100% rename from docs/endpoint_structure.md rename to docs/endpoint-structure.md diff --git a/src/database/helpers/agents.ts b/src/database/helpers/agents.ts index 0e40966..8cfb154 100644 --- a/src/database/helpers/agents.ts +++ b/src/database/helpers/agents.ts @@ -4,10 +4,10 @@ import { userAgentsModel, userTasksModel, UserAgentsTable } from '../models'; /** AGENT MANAGEMENT */ interface AgentResult { - success: boolean; - error?: string; - agent?: UserAgentsTable; - agents?: UserAgentsTable[]; + success: boolean; + error?: string; + agent?: UserAgentsTable; + agents?: UserAgentsTable[]; } /** @@ -18,30 +18,30 @@ interface AgentResult { * @throws Error if database query fails */ export async function getAgents(orm: D1Orm, crewId: number): Promise { - try { - userAgentsModel.SetOrm(orm); - const result = await userAgentsModel.All({ - where: { - crew_id: crewId - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); - return { - agents: result.results as unknown as UserAgentsTable[], - success: true - }; - } catch (error) { - console.error(`Error in getAgents: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to get agents: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userAgentsModel.SetOrm(orm); + const result = await userAgentsModel.All({ + where: { + crew_id: crewId, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); + return { + agents: result.results as unknown as UserAgentsTable[], + success: true, + }; + } catch (error) { + console.error(`Error in getAgents: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to get agents: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -51,24 +51,21 @@ export async function getAgents(orm: D1Orm, crewId: number): Promise -): Promise { - try { - userAgentsModel.SetOrm(orm); - const agent = await userAgentsModel.InsertOne(agentData); - return { - agent: agent as unknown as UserAgentsTable, - success: true - }; - } catch (error) { - console.error(`Error in createAgent: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to create agent: ${error instanceof Error ? error.message : String(error)}` - }; - } +export async function createAgent(orm: D1Orm, agentData: Omit): Promise { + try { + userAgentsModel.SetOrm(orm); + const agent = await userAgentsModel.InsertOne(agentData); + return { + agent: agent as unknown as UserAgentsTable, + success: true, + }; + } catch (error) { + console.error(`Error in createAgent: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to create agent: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -80,28 +77,28 @@ export async function createAgent( * @throws Error if database update fails */ export async function updateAgent( - orm: D1Orm, - agentId: number, - updates: Partial> + orm: D1Orm, + agentId: number, + updates: Partial> ): Promise { - try { - userAgentsModel.SetOrm(orm); - await userAgentsModel.Update({ - where: { - id: agentId - }, - data: updates - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in updateAgent: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to update agent: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userAgentsModel.SetOrm(orm); + await userAgentsModel.Update({ + where: { + id: agentId, + }, + data: updates, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in updateAgent: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to update agent: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -111,36 +108,39 @@ export async function updateAgent( * @returns Promise containing the deletion result or error details * @throws Error if database deletion fails */ -export async function deleteAgent(orm: D1Orm, agentId: number): Promise<{ - success: boolean; - error?: string; +export async function deleteAgent( + orm: D1Orm, + agentId: number +): Promise<{ + success: boolean; + error?: string; }> { - try { - userTasksModel.SetOrm(orm); - userAgentsModel.SetOrm(orm); - - // First delete all tasks associated with this agent - await userTasksModel.Delete({ - where: { - agent_id: agentId - } - }); - - // Then delete the agent - await userAgentsModel.Delete({ - where: { - id: agentId - } - }); - - return { - success: true - }; - } catch (error) { - console.error(`Error in deleteAgent: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to delete agent: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userTasksModel.SetOrm(orm); + userAgentsModel.SetOrm(orm); + + // First delete all tasks associated with this agent + await userTasksModel.Delete({ + where: { + agent_id: agentId, + }, + }); + + // Then delete the agent + await userAgentsModel.Delete({ + where: { + id: agentId, + }, + }); + + return { + success: true, + }; + } catch (error) { + console.error(`Error in deleteAgent: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to delete agent: ${error instanceof Error ? error.message : String(error)}`, + }; + } } diff --git a/src/database/helpers/conversations.ts b/src/database/helpers/conversations.ts index f056477..350ed04 100644 --- a/src/database/helpers/conversations.ts +++ b/src/database/helpers/conversations.ts @@ -1,20 +1,20 @@ import { D1Orm } from 'd1-orm'; -import { - userConversationsModel, - userCrewExecutionsModel, - userCrewExecutionStepsModel, - UserConversationsTable, - UserCrewExecutionsTable, - UserCrewExecutionStepsTable +import { + userConversationsModel, + userCrewExecutionsModel, + userCrewExecutionStepsModel, + UserConversationsTable, + UserCrewExecutionsTable, + UserCrewExecutionStepsTable, } from '../models'; /** CONVERSATION MANAGEMENT */ interface ConversationResult { - conversation?: UserConversationsTable; - conversations?: UserConversationsTable[]; - success: boolean; - error?: string; + conversation?: UserConversationsTable; + conversations?: UserConversationsTable[]; + success: boolean; + error?: string; } /** @@ -25,28 +25,24 @@ interface ConversationResult { * @returns Promise containing the created conversation or error details * @throws Error if database insertion fails */ -export async function addConversation( - orm: D1Orm, - address: string, - name: string = 'New Conversation' -): Promise { - try { - userConversationsModel.SetOrm(orm); - const conversation = await userConversationsModel.InsertOne({ - profile_id: address, - conversation_name: name, - }); - return { - conversation: conversation as unknown as UserConversationsTable, - success: true - }; - } catch (error) { - console.error(`Error in addConversation: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to create conversation: ${error instanceof Error ? error.message : String(error)}` - }; - } +export async function addConversation(orm: D1Orm, address: string, name: string = 'New Conversation'): Promise { + try { + userConversationsModel.SetOrm(orm); + const conversation = await userConversationsModel.InsertOne({ + profile_id: address, + conversation_name: name, + }); + return { + conversation: conversation as unknown as UserConversationsTable, + success: true, + }; + } catch (error) { + console.error(`Error in addConversation: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to create conversation: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -58,33 +54,28 @@ export async function addConversation( * @returns Promise containing the update result or error details * @throws Error if database update fails */ -export async function updateConversation( - orm: D1Orm, - address: string, - conversationId: number, - name?: string -): Promise { - try { - userConversationsModel.SetOrm(orm); - await userConversationsModel.Update({ - where: { - id: conversationId, - profile_id: address, - }, - data: { - conversation_name: name, - }, - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in updateConversation: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to update conversation: ${error instanceof Error ? error.message : String(error)}` - }; - } +export async function updateConversation(orm: D1Orm, address: string, conversationId: number, name?: string): Promise { + try { + userConversationsModel.SetOrm(orm); + await userConversationsModel.Update({ + where: { + id: conversationId, + profile_id: address, + }, + data: { + conversation_name: name, + }, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in updateConversation: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to update conversation: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -95,29 +86,25 @@ export async function updateConversation( * @returns Promise containing the deletion result or error details * @throws Error if database deletion fails */ -export async function deleteConversation( - orm: D1Orm, - address: string, - conversationId: number -): Promise { - try { - userConversationsModel.SetOrm(orm); - await userConversationsModel.Delete({ - where: { - id: conversationId, - profile_id: address, - }, - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in deleteConversation: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to delete conversation: ${error instanceof Error ? error.message : String(error)}` - }; - } +export async function deleteConversation(orm: D1Orm, address: string, conversationId: number): Promise { + try { + userConversationsModel.SetOrm(orm); + await userConversationsModel.Delete({ + where: { + id: conversationId, + profile_id: address, + }, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in deleteConversation: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to delete conversation: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -127,28 +114,25 @@ export async function deleteConversation( * @returns Promise containing the conversation data or error details * @throws Error if database query fails */ -export async function getConversation( - orm: D1Orm, - conversationId: number -): Promise { - try { - userConversationsModel.SetOrm(orm); - const conversation = await userConversationsModel.First({ - where: { - id: conversationId, - }, - }); - return { - conversation: conversation as unknown as UserConversationsTable, - success: true - }; - } catch (error) { - console.error(`Error in getConversation: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to get conversation: ${error instanceof Error ? error.message : String(error)}` - }; - } +export async function getConversation(orm: D1Orm, conversationId: number): Promise { + try { + userConversationsModel.SetOrm(orm); + const conversation = await userConversationsModel.First({ + where: { + id: conversationId, + }, + }); + return { + conversation: conversation as unknown as UserConversationsTable, + success: true, + }; + } catch (error) { + console.error(`Error in getConversation: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to get conversation: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -159,50 +143,50 @@ export async function getConversation( * @throws Error if database query fails */ export async function getConversationWithExecutions( - orm: D1Orm, - conversationId: number + orm: D1Orm, + conversationId: number ): Promise { - try { - userConversationsModel.SetOrm(orm); - userCrewExecutionsModel.SetOrm(orm); + try { + userConversationsModel.SetOrm(orm); + userCrewExecutionsModel.SetOrm(orm); - const conversation = await userConversationsModel.First({ - where: { - id: conversationId, - }, - }); + const conversation = await userConversationsModel.First({ + where: { + id: conversationId, + }, + }); - if (!conversation) { - return { - success: false, - error: `Conversation with ID ${conversationId} not found` - }; - } + if (!conversation) { + return { + success: false, + error: `Conversation with ID ${conversationId} not found`, + }; + } - const executions = await userCrewExecutionsModel.All({ - where: { - conversation_id: conversationId, - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); + const executions = await userCrewExecutionsModel.All({ + where: { + conversation_id: conversationId, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); - return { - conversation: conversation as UserConversationsTable, - executions: executions.results, - success: true - }; - } catch (error) { - console.error(`Error in getConversationWithExecutions: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to get conversation with executions: ${error instanceof Error ? error.message : String(error)}` - }; - } + return { + conversation: conversation as UserConversationsTable, + executions: executions.results, + success: true, + }; + } catch (error) { + console.error(`Error in getConversationWithExecutions: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to get conversation with executions: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -212,35 +196,32 @@ export async function getConversationWithExecutions( * @returns Promise containing array of conversations or error details * @throws Error if database query fails */ -export async function getConversations( - orm: D1Orm, - address: string -): Promise { - try { - userConversationsModel.SetOrm(orm); - const result = await userConversationsModel.All({ - where: { - profile_id: address, - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); - return { - conversations: result.results as unknown as UserConversationsTable[], - success: true - }; - } catch (error) { - console.error(`Error in getConversations: ${error instanceof Error ? error.message : String(error)}`); - return { - conversations: [], - success: false, - error: `Failed to get conversations: ${error instanceof Error ? error.message : String(error)}` - }; - } +export async function getConversations(orm: D1Orm, address: string): Promise { + try { + userConversationsModel.SetOrm(orm); + const result = await userConversationsModel.All({ + where: { + profile_id: address, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); + return { + conversations: result.results as unknown as UserConversationsTable[], + success: true, + }; + } catch (error) { + console.error(`Error in getConversations: ${error instanceof Error ? error.message : String(error)}`); + return { + conversations: [], + success: false, + error: `Failed to get conversations: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -251,57 +232,57 @@ export async function getConversations( * @throws Error if database query fails */ export async function getConversationsWithExecutions( - orm: D1Orm, - address: string + orm: D1Orm, + address: string ): Promise }> { - try { - userConversationsModel.SetOrm(orm); - userCrewExecutionsModel.SetOrm(orm); + try { + userConversationsModel.SetOrm(orm); + userCrewExecutionsModel.SetOrm(orm); - const conversations = await userConversationsModel.All({ - where: { - profile_id: address, - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); + const conversations = await userConversationsModel.All({ + where: { + profile_id: address, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); - const details = []; - for (const conversation of conversations.results) { - const executions = await userCrewExecutionsModel.All({ - where: { - conversation_id: conversation.id, - profile_id: address, - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); + const details = []; + for (const conversation of conversations.results) { + const executions = await userCrewExecutionsModel.All({ + where: { + conversation_id: conversation.id, + profile_id: address, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); - details.push({ - conversation: conversation as UserConversationsTable, - executions: executions.results, - }); - } + details.push({ + conversation: conversation as UserConversationsTable, + executions: executions.results, + }); + } - return { - details, - success: true - }; - } catch (error) { - console.error(`Error in getConversationsWithExecutions: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to get conversations with executions: ${error instanceof Error ? error.message : String(error)}` - }; - } + return { + details, + success: true, + }; + } catch (error) { + console.error(`Error in getConversationsWithExecutions: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to get conversations with executions: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -311,42 +292,39 @@ export async function getConversationsWithExecutions( * @returns Promise containing the most recent conversation or error details * @throws Error if database query fails */ -export async function getLatestConversation( - orm: D1Orm, - address: string -): Promise { - try { - userConversationsModel.SetOrm(orm); - const result = await userConversationsModel.All({ - where: { - profile_id: address, - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - limit: 1, - }); +export async function getLatestConversation(orm: D1Orm, address: string): Promise { + try { + userConversationsModel.SetOrm(orm); + const result = await userConversationsModel.All({ + where: { + profile_id: address, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + limit: 1, + }); - if (result.results.length === 0) { - return { - success: true - }; - } + if (result.results.length === 0) { + return { + success: true, + }; + } - return { - conversation: result.results[0] as unknown as UserConversationsTable, - success: true - }; - } catch (error) { - console.error(`Error in getLatestConversation: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to get latest conversation: ${error instanceof Error ? error.message : String(error)}` - }; - } + return { + conversation: result.results[0] as unknown as UserConversationsTable, + success: true, + }; + } catch (error) { + console.error(`Error in getLatestConversation: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to get latest conversation: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -356,42 +334,39 @@ export async function getLatestConversation( * @returns Promise containing the conversation ID or error details * @throws Error if database query fails */ -export async function getLatestConversationId( - orm: D1Orm, - address: string -): Promise<{ id?: number; success: boolean; error?: string }> { - try { - const result = await getLatestConversation(orm, address); - if (!result.success) { - return result; - } - - if (!result.conversation) { - return { - success: true - }; - } +export async function getLatestConversationId(orm: D1Orm, address: string): Promise<{ id?: number; success: boolean; error?: string }> { + try { + const result = await getLatestConversation(orm, address); + if (!result.success) { + return result; + } - return { - id: result.conversation.id, - success: true - }; - } catch (error) { - console.error(`Error in getLatestConversationId: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to get latest conversation ID: ${error instanceof Error ? error.message : String(error)}` - }; - } + if (!result.conversation) { + return { + success: true, + }; + } + + return { + id: result.conversation.id, + success: true, + }; + } catch (error) { + console.error(`Error in getLatestConversationId: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to get latest conversation ID: ${error instanceof Error ? error.message : String(error)}`, + }; + } } interface ConversationHistoryResult { - history?: Array<{ - execution: UserCrewExecutionsTable; - steps: UserCrewExecutionStepsTable[]; - }>; - success: boolean; - error?: string; + history?: Array<{ + execution: UserCrewExecutionsTable; + steps: UserCrewExecutionStepsTable[]; + }>; + success: boolean; + error?: string; } /** @@ -401,58 +376,55 @@ interface ConversationHistoryResult { * @returns Promise containing the conversation history or error details * @throws Error if database query fails */ -export async function getConversationHistory( - orm: D1Orm, - conversationId: number -): Promise { - try { - userConversationsModel.SetOrm(orm); - userCrewExecutionsModel.SetOrm(orm); - userCrewExecutionStepsModel.SetOrm(orm); +export async function getConversationHistory(orm: D1Orm, conversationId: number): Promise { + try { + userConversationsModel.SetOrm(orm); + userCrewExecutionsModel.SetOrm(orm); + userCrewExecutionStepsModel.SetOrm(orm); - // Get all executions for this conversation - const executions = await userCrewExecutionsModel.All({ - where: { - conversation_id: conversationId, - }, - orderBy: [ - { - column: 'created_at', - descending: false, - }, - ], - }); + // Get all executions for this conversation + const executions = await userCrewExecutionsModel.All({ + where: { + conversation_id: conversationId, + }, + orderBy: [ + { + column: 'created_at', + descending: false, + }, + ], + }); - const history = []; - for (const execution of executions.results) { - // Get all steps for this execution - const steps = await userCrewExecutionStepsModel.All({ - where: { - execution_id: execution.id, - }, - orderBy: [ - { - column: 'created_at', - descending: false, - }, - ], - }); + const history = []; + for (const execution of executions.results) { + // Get all steps for this execution + const steps = await userCrewExecutionStepsModel.All({ + where: { + execution_id: execution.id, + }, + orderBy: [ + { + column: 'created_at', + descending: false, + }, + ], + }); - history.push({ - execution, - steps: steps.results, - }); - } + history.push({ + execution, + steps: steps.results, + }); + } - return { - history, - success: true - }; - } catch (error) { - console.error(`Error in getConversationHistory: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to get conversation history: ${error instanceof Error ? error.message : String(error)}` - }; - } + return { + history, + success: true, + }; + } catch (error) { + console.error(`Error in getConversationHistory: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to get conversation history: ${error instanceof Error ? error.message : String(error)}`, + }; + } } diff --git a/src/database/helpers/crews.ts b/src/database/helpers/crews.ts index f733e6d..685b6a4 100644 --- a/src/database/helpers/crews.ts +++ b/src/database/helpers/crews.ts @@ -4,12 +4,12 @@ import { userCrewExecutionsModel, userCrewsModel, UserCrewsTable, UserCrewExecut /** CREW MANAGEMENT */ interface CrewResult { - crew?: UserCrewsTable; - crews?: UserCrewsTable[]; - execution?: UserCrewExecutionsTable; - executions?: UserCrewExecutionsTable[]; - success: boolean; - error?: string; + crew?: UserCrewsTable; + crews?: UserCrewsTable[]; + execution?: UserCrewExecutionsTable; + executions?: UserCrewExecutionsTable[]; + success: boolean; + error?: string; } /** @@ -23,33 +23,33 @@ interface CrewResult { * @throws Error if database insertion fails */ export async function addCrewExecution( - orm: D1Orm, - address: string, - crewId: number, - conversationId: number, - input?: string + orm: D1Orm, + address: string, + crewId: number, + conversationId: number, + input?: string ): Promise { - try { - userCrewExecutionsModel.SetOrm(orm); - const execution = await userCrewExecutionsModel.InsertOne({ - profile_id: address, - crew_id: crewId, - conversation_id: conversationId, - user_input: input, - total_tokens: 0, - successful_requests: 0, - }); - return { - execution: execution as unknown as UserCrewExecutionsTable, - success: true - }; - } catch (error) { - console.error(`Error in addCrewExecution: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to create crew execution: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCrewExecutionsModel.SetOrm(orm); + const execution = await userCrewExecutionsModel.InsertOne({ + profile_id: address, + crew_id: crewId, + conversation_id: conversationId, + user_input: input, + total_tokens: 0, + successful_requests: 0, + }); + return { + execution: execution as unknown as UserCrewExecutionsTable, + success: true, + }; + } catch (error) { + console.error(`Error in addCrewExecution: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to create crew execution: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -60,31 +60,31 @@ export async function addCrewExecution( * @throws Error if database query fails */ export async function getCrewExecutions(orm: D1Orm, address: string): Promise { - try { - userCrewExecutionsModel.SetOrm(orm); - const result = await userCrewExecutionsModel.All({ - where: { - profile_id: address, - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); - return { - executions: result.results as unknown as UserCrewExecutionsTable[], - success: true - }; - } catch (error) { - console.error(`Error in getCrewExecutions: ${error instanceof Error ? error.message : String(error)}`); - return { - executions: [], - success: false, - error: `Failed to get crew executions: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCrewExecutionsModel.SetOrm(orm); + const result = await userCrewExecutionsModel.All({ + where: { + profile_id: address, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); + return { + executions: result.results as unknown as UserCrewExecutionsTable[], + success: true, + }; + } catch (error) { + console.error(`Error in getCrewExecutions: ${error instanceof Error ? error.message : String(error)}`); + return { + executions: [], + success: false, + error: `Failed to get crew executions: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -94,31 +94,31 @@ export async function getCrewExecutions(orm: D1Orm, address: string): Promise { - try { - userCrewsModel.SetOrm(orm); - const result = await userCrewsModel.All({ - where: { - crew_is_public: 1, - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); - return { - crews: result.results as unknown as UserCrewsTable[], - success: true - }; - } catch (error) { - console.error(`Error in getPublicCrews: ${error instanceof Error ? error.message : String(error)}`); - return { - crews: [], - success: false, - error: `Failed to get public crews: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCrewsModel.SetOrm(orm); + const result = await userCrewsModel.All({ + where: { + crew_is_public: 1, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); + return { + crews: result.results as unknown as UserCrewsTable[], + success: true, + }; + } catch (error) { + console.error(`Error in getPublicCrews: ${error instanceof Error ? error.message : String(error)}`); + return { + crews: [], + success: false, + error: `Failed to get public crews: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -129,24 +129,24 @@ export async function getPublicCrews(orm: D1Orm): Promise { * @throws Error if database query fails */ export async function getCrew(orm: D1Orm, crewId: number): Promise { - try { - userCrewsModel.SetOrm(orm); - const crew = await userCrewsModel.First({ - where: { - id: crewId - } - }); - return { - crew: crew as unknown as UserCrewsTable, - success: true - }; - } catch (error) { - console.error(`Error in getCrew: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to get crew: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCrewsModel.SetOrm(orm); + const crew = await userCrewsModel.First({ + where: { + id: crewId, + }, + }); + return { + crew: crew as unknown as UserCrewsTable, + success: true, + }; + } catch (error) { + console.error(`Error in getCrew: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to get crew: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -156,21 +156,24 @@ export async function getCrew(orm: D1Orm, crewId: number): Promise { * @returns Promise containing the created crew or error details * @throws Error if database insertion fails */ -export async function createCrew(orm: D1Orm, crewData: Omit): Promise { - try { - userCrewsModel.SetOrm(orm); - const crew = await userCrewsModel.InsertOne(crewData); - return { - crew: crew as unknown as UserCrewsTable, - success: true - }; - } catch (error) { - console.error(`Error in createCrew: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to create crew: ${error instanceof Error ? error.message : String(error)}` - }; - } +export async function createCrew( + orm: D1Orm, + crewData: Omit +): Promise { + try { + userCrewsModel.SetOrm(orm); + const crew = await userCrewsModel.InsertOne(crewData); + return { + crew: crew as unknown as UserCrewsTable, + success: true, + }; + } catch (error) { + console.error(`Error in createCrew: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to create crew: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -182,28 +185,28 @@ export async function createCrew(orm: D1Orm, crewData: Omit> + orm: D1Orm, + crewId: number, + updates: Partial> ): Promise { - try { - userCrewsModel.SetOrm(orm); - await userCrewsModel.Update({ - where: { - id: crewId - }, - data: updates - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in updateCrew: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to update crew: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCrewsModel.SetOrm(orm); + await userCrewsModel.Update({ + where: { + id: crewId, + }, + data: updates, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in updateCrew: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to update crew: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -214,29 +217,29 @@ export async function updateCrew( * @throws Error if database deletion fails * @note Due to CASCADE DELETE in schema, deleting the crew will automatically delete: * - Associated agents (user_agents) - * - Associated tasks (user_tasks) + * - Associated tasks (user_tasks) * - Associated executions (user_crew_executions) * - Associated execution steps (user_crew_execution_steps) * - Associated crons (user_crons) */ export async function deleteCrew(orm: D1Orm, crewId: number): Promise { - try { - userCrewsModel.SetOrm(orm); - await userCrewsModel.Delete({ - where: { - id: crewId - } - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in deleteCrew: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to delete crew: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCrewsModel.SetOrm(orm); + await userCrewsModel.Delete({ + where: { + id: crewId, + }, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in deleteCrew: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to delete crew: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -253,31 +256,31 @@ export async function deleteCrew(orm: D1Orm, crewId: number): Promise { - try { - userCrewExecutionStepsModel.SetOrm(orm); - const result = await userCrewExecutionStepsModel.All({ - where: { - execution_id: executionId - }, - orderBy: [ - { - column: 'created_at', - ascending: true, - }, - ], - }); - return { - steps: result.results as unknown as UserCrewExecutionStepsTable[], - success: true - }; - } catch (error) { - console.error(`Error in getExecutionSteps: ${error instanceof Error ? error.message : String(error)}`); - return { - steps: [], - success: false, - error: `Failed to get execution steps: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCrewExecutionStepsModel.SetOrm(orm); + const result = await userCrewExecutionStepsModel.All({ + where: { + execution_id: executionId, + }, + orderBy: [ + { + column: 'created_at', + ascending: true, + }, + ], + }); + return { + steps: result.results as unknown as UserCrewExecutionStepsTable[], + success: true, + }; + } catch (error) { + console.error(`Error in getExecutionSteps: ${error instanceof Error ? error.message : String(error)}`); + return { + steps: [], + success: false, + error: `Failed to get execution steps: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -287,23 +290,23 @@ export async function getExecutionSteps(orm: D1Orm, executionId: number): Promis * @returns Promise containing the created step or error details */ export async function createExecutionStep( - orm: D1Orm, - stepData: Omit + orm: D1Orm, + stepData: Omit ): Promise { - try { - userCrewExecutionStepsModel.SetOrm(orm); - const step = await userCrewExecutionStepsModel.InsertOne(stepData); - return { - step: step as unknown as UserCrewExecutionStepsTable, - success: true - }; - } catch (error) { - console.error(`Error in createExecutionStep: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to create execution step: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCrewExecutionStepsModel.SetOrm(orm); + const step = await userCrewExecutionStepsModel.InsertOne(stepData); + return { + step: step as unknown as UserCrewExecutionStepsTable, + success: true, + }; + } catch (error) { + console.error(`Error in createExecutionStep: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to create execution step: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -313,49 +316,49 @@ export async function createExecutionStep( * @returns Promise containing the deletion result or error details */ export async function deleteExecutionSteps(orm: D1Orm, executionId: number): Promise { - try { - userCrewExecutionStepsModel.SetOrm(orm); - await userCrewExecutionStepsModel.Delete({ - where: { - execution_id: executionId - } - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in deleteExecutionSteps: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to delete execution steps: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCrewExecutionStepsModel.SetOrm(orm); + await userCrewExecutionStepsModel.Delete({ + where: { + execution_id: executionId, + }, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in deleteExecutionSteps: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to delete execution steps: ${error instanceof Error ? error.message : String(error)}`, + }; + } } export async function getCrewsByProfile(orm: D1Orm, address: string): Promise { - try { - userCrewsModel.SetOrm(orm); - const result = await userCrewsModel.All({ - where: { - profile_id: address - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); - return { - crews: result.results as unknown as UserCrewsTable[], - success: true - }; - } catch (error) { - console.error(`Error in getCrewsByProfile: ${error instanceof Error ? error.message : String(error)}`); - return { - crews: [], - success: false, - error: `Failed to get crews for profile: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCrewsModel.SetOrm(orm); + const result = await userCrewsModel.All({ + where: { + profile_id: address, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); + return { + crews: result.results as unknown as UserCrewsTable[], + success: true, + }; + } catch (error) { + console.error(`Error in getCrewsByProfile: ${error instanceof Error ? error.message : String(error)}`); + return { + crews: [], + success: false, + error: `Failed to get crews for profile: ${error instanceof Error ? error.message : String(error)}`, + }; + } } diff --git a/src/database/helpers/crons.ts b/src/database/helpers/crons.ts index 74fdbc1..c9a98d7 100644 --- a/src/database/helpers/crons.ts +++ b/src/database/helpers/crons.ts @@ -4,10 +4,10 @@ import { userAgentsModel, userCrewsModel, userCronsModel, userProfilesModel, use /** CRON MANAGEMENT */ interface CronResult { - cron?: UserCronsTable; - crons?: UserCronsTable[]; - success: boolean; - error?: string; + cron?: UserCronsTable; + crons?: UserCronsTable[]; + success: boolean; + error?: string; } /** @@ -17,31 +17,31 @@ interface CronResult { * @throws Error if database query fails */ export async function getEnabledCrons(orm: D1Orm): Promise { - try { - userCronsModel.SetOrm(orm); - const result = await userCronsModel.All({ - where: { - cron_enabled: 1, - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); - return { - crons: result.results as unknown as UserCronsTable[], - success: true - }; - } catch (error) { - console.error(`Error in getEnabledCrons: ${error instanceof Error ? error.message : String(error)}`); - return { - crons: [], - success: false, - error: `Failed to get enabled crons: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCronsModel.SetOrm(orm); + const result = await userCronsModel.All({ + where: { + cron_enabled: 1, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); + return { + crons: result.results as unknown as UserCronsTable[], + success: true, + }; + } catch (error) { + console.error(`Error in getEnabledCrons: ${error instanceof Error ? error.message : String(error)}`); + return { + crons: [], + success: false, + error: `Failed to get enabled crons: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -52,31 +52,31 @@ export async function getEnabledCrons(orm: D1Orm): Promise { * @throws Error if database query fails */ export async function getCronsByCrew(orm: D1Orm, crewId: number): Promise { - try { - userCronsModel.SetOrm(orm); - const result = await userCronsModel.All({ - where: { - crew_id: crewId - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); - return { - crons: result.results as unknown as UserCronsTable[], - success: true - }; - } catch (error) { - console.error(`Error in getCronsByCrew: ${error instanceof Error ? error.message : String(error)}`); - return { - crons: [], - success: false, - error: `Failed to get crons for crew: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCronsModel.SetOrm(orm); + const result = await userCronsModel.All({ + where: { + crew_id: crewId, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); + return { + crons: result.results as unknown as UserCronsTable[], + success: true, + }; + } catch (error) { + console.error(`Error in getCronsByCrew: ${error instanceof Error ? error.message : String(error)}`); + return { + crons: [], + success: false, + error: `Failed to get crons for crew: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -87,20 +87,20 @@ export async function getCronsByCrew(orm: D1Orm, crewId: number): Promise): Promise { - try { - userCronsModel.SetOrm(orm); - const cron = await userCronsModel.InsertOne(cronData); - return { - cron: cron as unknown as UserCronsTable, - success: true - }; - } catch (error) { - console.error(`Error in createCron: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to create cron: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCronsModel.SetOrm(orm); + const cron = await userCronsModel.InsertOne(cronData); + return { + cron: cron as unknown as UserCronsTable, + success: true, + }; + } catch (error) { + console.error(`Error in createCron: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to create cron: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -112,26 +112,26 @@ export async function createCron(orm: D1Orm, cronData: Omit { - try { - userCronsModel.SetOrm(orm); - await userCronsModel.Update({ - where: { - id: cronId - }, - data: { - cron_input: cronInput - } - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in updateCronInput: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to update cron input: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCronsModel.SetOrm(orm); + await userCronsModel.Update({ + where: { + id: cronId, + }, + data: { + cron_input: cronInput, + }, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in updateCronInput: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to update cron input: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -143,33 +143,33 @@ export async function updateCronInput(orm: D1Orm, cronId: number, cronInput: str * @throws Error if database update fails */ export async function toggleCronStatus(orm: D1Orm, cronId: number, enabled: 0 | 1): Promise { - try { - userCronsModel.SetOrm(orm); - await userCronsModel.Update({ - where: { - id: cronId - }, - data: { - cron_enabled: enabled - } - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in toggleCronStatus: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to toggle cron status: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userCronsModel.SetOrm(orm); + await userCronsModel.Update({ + where: { + id: cronId, + }, + data: { + cron_enabled: enabled, + }, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in toggleCronStatus: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to toggle cron status: ${error instanceof Error ? error.message : String(error)}`, + }; + } } interface DetailedCronData { - crew: any; - agents: any[]; - tasks: any[]; - profile: any; + crew: any; + agents: any[]; + tasks: any[]; + profile: any; } /** @@ -179,84 +179,84 @@ interface DetailedCronData { * @throws Error if database query fails */ export async function getEnabledCronsDetailed(orm: D1Orm): Promise<{ - details?: DetailedCronData[]; - success: boolean; - error?: string; + details?: DetailedCronData[]; + success: boolean; + error?: string; }> { - try { - userCronsModel.SetOrm(orm); - userCrewsModel.SetOrm(orm); - userAgentsModel.SetOrm(orm); - userTasksModel.SetOrm(orm); - userProfilesModel.SetOrm(orm); + try { + userCronsModel.SetOrm(orm); + userCrewsModel.SetOrm(orm); + userAgentsModel.SetOrm(orm); + userTasksModel.SetOrm(orm); + userProfilesModel.SetOrm(orm); - const crons = await userCronsModel.All({ - where: { - cron_enabled: 1, - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); + const crons = await userCronsModel.All({ + where: { + cron_enabled: 1, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); - const details: DetailedCronData[] = []; + const details: DetailedCronData[] = []; - // Iterate through all cron entries returned - for (const cron of crons.results) { - // Get the related crew info for this cron - const crew = await userCrewsModel.First({ - where: { - id: cron.crew_id, - }, - }); + // Iterate through all cron entries returned + for (const cron of crons.results) { + // Get the related crew info for this cron + const crew = await userCrewsModel.First({ + where: { + id: cron.crew_id, + }, + }); - // Get all of the agents for the crew - const agents = await userAgentsModel.All({ - where: { - profile_id: cron.profile_id, - crew_id: cron.crew_id, - }, - }); + // Get all of the agents for the crew + const agents = await userAgentsModel.All({ + where: { + profile_id: cron.profile_id, + crew_id: cron.crew_id, + }, + }); - // Get all of the tasks for each agent - const tasks = []; - for (const agent of agents.results) { - const agentTasks = await userTasksModel.All({ - where: { - profile_id: cron.profile_id, - crew_id: cron.crew_id, - agent_id: agent.id, - }, - }); - tasks.push(agentTasks.results); - } + // Get all of the tasks for each agent + const tasks = []; + for (const agent of agents.results) { + const agentTasks = await userTasksModel.All({ + where: { + profile_id: cron.profile_id, + crew_id: cron.crew_id, + agent_id: agent.id, + }, + }); + tasks.push(agentTasks.results); + } - const profile = await userProfilesModel.First({ - where: { - stx_address: cron.profile_id, - }, - }); + const profile = await userProfilesModel.First({ + where: { + stx_address: cron.profile_id, + }, + }); - details.push({ - crew, - agents: agents.results, - tasks, - profile, - }); - } + details.push({ + crew, + agents: agents.results, + tasks, + profile, + }); + } - return { - details, - success: true - }; - } catch (error) { - console.error(`Error in getEnabledCronsDetailed: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to get detailed cron information: ${error instanceof Error ? error.message : String(error)}` - }; - } + return { + details, + success: true, + }; + } catch (error) { + console.error(`Error in getEnabledCronsDetailed: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to get detailed cron information: ${error instanceof Error ? error.message : String(error)}`, + }; + } } diff --git a/src/database/helpers/index.ts b/src/database/helpers/index.ts index d82299b..c242258 100644 --- a/src/database/helpers/index.ts +++ b/src/database/helpers/index.ts @@ -1,7 +1,7 @@ -export * from './agents' -export * from './crews' -export * from './crons' -export * from './conversations' -export * from './profiles' -export * from './tasks' -export * from './twitter' +export * from './agents'; +export * from './crews'; +export * from './crons'; +export * from './conversations'; +export * from './profiles'; +export * from './tasks'; +export * from './twitter'; diff --git a/src/database/helpers/profiles.ts b/src/database/helpers/profiles.ts index 133cbc4..b1ce4d1 100644 --- a/src/database/helpers/profiles.ts +++ b/src/database/helpers/profiles.ts @@ -4,10 +4,10 @@ import { userProfilesModel, UserProfilesTable } from '../models'; /** PROFILE MANAGEMENT */ interface ProfileResult { - profile?: UserProfilesTable; - profiles?: UserProfilesTable[]; - success: boolean; - error?: string; + profile?: UserProfilesTable; + profiles?: UserProfilesTable[]; + success: boolean; + error?: string; } /** @@ -17,25 +17,25 @@ interface ProfileResult { * @returns Promise containing the user's role or error details * @throws Error if database query fails */ -export async function getUserRole(orm: D1Orm, address: string): Promise<{success: boolean; role?: string; error?: string}> { - try { - userProfilesModel.SetOrm(orm); - const profile = await userProfilesModel.First({ - where: { - stx_address: address - } - }); - return { - role: profile ? profile.user_role : undefined, - success: true - }; - } catch (error) { - console.error(`Error in getUserRole: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to get user role: ${error instanceof Error ? error.message : String(error)}` - }; - } +export async function getUserRole(orm: D1Orm, address: string): Promise<{ success: boolean; role?: string; error?: string }> { + try { + userProfilesModel.SetOrm(orm); + const profile = await userProfilesModel.First({ + where: { + stx_address: address, + }, + }); + return { + role: profile ? profile.user_role : undefined, + success: true, + }; + } catch (error) { + console.error(`Error in getUserRole: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to get user role: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -46,24 +46,24 @@ export async function getUserRole(orm: D1Orm, address: string): Promise<{success * @throws Error if database query fails */ export async function getUserProfile(orm: D1Orm, address: string): Promise { - try { - userProfilesModel.SetOrm(orm); - const profile = await userProfilesModel.First({ - where: { - stx_address: address - } - }); - return { - profile: profile as unknown as UserProfilesTable, - success: true - }; - } catch (error) { - console.error(`Error in getUserProfile: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to get user profile: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userProfilesModel.SetOrm(orm); + const profile = await userProfilesModel.First({ + where: { + stx_address: address, + }, + }); + return { + profile: profile as unknown as UserProfilesTable, + success: true, + }; + } catch (error) { + console.error(`Error in getUserProfile: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to get user profile: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -74,23 +74,23 @@ export async function getUserProfile(orm: D1Orm, address: string): Promise + orm: D1Orm, + profileData: Omit ): Promise { - try { - userProfilesModel.SetOrm(orm); - const profile = await userProfilesModel.InsertOne(profileData); - return { - profile: profile as unknown as UserProfilesTable, - success: true - }; - } catch (error) { - console.error(`Error in createUserProfile: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to create user profile: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userProfilesModel.SetOrm(orm); + const profile = await userProfilesModel.InsertOne(profileData); + return { + profile: profile as unknown as UserProfilesTable, + success: true, + }; + } catch (error) { + console.error(`Error in createUserProfile: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to create user profile: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -102,28 +102,28 @@ export async function createUserProfile( * @throws Error if database update fails */ export async function updateUserProfile( - orm: D1Orm, - address: string, - profileData: Partial> + orm: D1Orm, + address: string, + profileData: Partial> ): Promise { - try { - userProfilesModel.SetOrm(orm); - await userProfilesModel.Update({ - where: { - stx_address: address - }, - data: profileData - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in updateUserProfile: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to update user profile: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userProfilesModel.SetOrm(orm); + await userProfilesModel.Update({ + where: { + stx_address: address, + }, + data: profileData, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in updateUserProfile: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to update user profile: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -134,23 +134,23 @@ export async function updateUserProfile( * @throws Error if database deletion fails */ export async function deleteUserProfile(orm: D1Orm, address: string): Promise { - try { - userProfilesModel.SetOrm(orm); - await userProfilesModel.Delete({ - where: { - stx_address: address - } - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in deleteUserProfile: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to delete user profile: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userProfilesModel.SetOrm(orm); + await userProfilesModel.Delete({ + where: { + stx_address: address, + }, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in deleteUserProfile: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to delete user profile: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -160,28 +160,28 @@ export async function deleteUserProfile(orm: D1Orm, address: string): Promise { - try { - userProfilesModel.SetOrm(orm); - const result = await userProfilesModel.All({ - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); - return { - profiles: result.results as unknown as UserProfilesTable[], - success: true - }; - } catch (error) { - console.error(`Error in getAllUserProfiles: ${error instanceof Error ? error.message : String(error)}`); - return { - profiles: [], - success: false, - error: `Failed to get all user profiles: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userProfilesModel.SetOrm(orm); + const result = await userProfilesModel.All({ + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); + return { + profiles: result.results as unknown as UserProfilesTable[], + success: true, + }; + } catch (error) { + console.error(`Error in getAllUserProfiles: ${error instanceof Error ? error.message : String(error)}`); + return { + profiles: [], + success: false, + error: `Failed to get all user profiles: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -193,26 +193,26 @@ export async function getAllUserProfiles(orm: D1Orm): Promise { * @throws Error if database update fails */ export async function updateUserProfileById( - orm: D1Orm, - userId: number, - updates: Partial> + orm: D1Orm, + userId: number, + updates: Partial> ): Promise { - try { - userProfilesModel.SetOrm(orm); - await userProfilesModel.Update({ - where: { - id: userId - }, - data: updates - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in updateUserProfileById: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to update user profile by ID: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userProfilesModel.SetOrm(orm); + await userProfilesModel.Update({ + where: { + id: userId, + }, + data: updates, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in updateUserProfileById: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to update user profile by ID: ${error instanceof Error ? error.message : String(error)}`, + }; + } } diff --git a/src/database/helpers/tasks.ts b/src/database/helpers/tasks.ts index fa61d03..9860303 100644 --- a/src/database/helpers/tasks.ts +++ b/src/database/helpers/tasks.ts @@ -4,10 +4,10 @@ import { userTasksModel, UserTasksTable } from '../models'; /** TASK MANAGEMENT */ interface TaskResult { - task?: UserTasksTable; - tasks?: UserTasksTable[]; - success: boolean; - error?: string; + task?: UserTasksTable; + tasks?: UserTasksTable[]; + success: boolean; + error?: string; } /** @@ -18,24 +18,24 @@ interface TaskResult { * @throws Error if database query fails */ export async function getTask(orm: D1Orm, taskId: number): Promise { - try { - userTasksModel.SetOrm(orm); - const task = await userTasksModel.First({ - where: { - id: taskId - } - }); - return { - task: task as unknown as UserTasksTable, - success: true - }; - } catch (error) { - console.error(`Error in getTask: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to get task: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userTasksModel.SetOrm(orm); + const task = await userTasksModel.First({ + where: { + id: taskId, + }, + }); + return { + task: task as unknown as UserTasksTable, + success: true, + }; + } catch (error) { + console.error(`Error in getTask: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to get task: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -46,31 +46,31 @@ export async function getTask(orm: D1Orm, taskId: number): Promise { * @throws Error if database query fails */ export async function getTasks(orm: D1Orm, agentId: number): Promise { - try { - userTasksModel.SetOrm(orm); - const result = await userTasksModel.All({ - where: { - agent_id: agentId - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); - return { - tasks: result.results as unknown as UserTasksTable[], - success: true - }; - } catch (error) { - console.error(`Error in getTasks: ${error instanceof Error ? error.message : String(error)}`); - return { - tasks: [], - success: false, - error: `Failed to get tasks: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userTasksModel.SetOrm(orm); + const result = await userTasksModel.All({ + where: { + agent_id: agentId, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); + return { + tasks: result.results as unknown as UserTasksTable[], + success: true, + }; + } catch (error) { + console.error(`Error in getTasks: ${error instanceof Error ? error.message : String(error)}`); + return { + tasks: [], + success: false, + error: `Failed to get tasks: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -81,20 +81,20 @@ export async function getTasks(orm: D1Orm, agentId: number): Promise * @throws Error if database insertion fails */ export async function createTask(orm: D1Orm, taskData: Omit): Promise { - try { - userTasksModel.SetOrm(orm); - const task = await userTasksModel.InsertOne(taskData); - return { - task: task as unknown as UserTasksTable, - success: true - }; - } catch (error) { - console.error(`Error in createTask: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to create task: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userTasksModel.SetOrm(orm); + const task = await userTasksModel.InsertOne(taskData); + return { + task: task as unknown as UserTasksTable, + success: true, + }; + } catch (error) { + console.error(`Error in createTask: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to create task: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -105,25 +105,29 @@ export async function createTask(orm: D1Orm, taskData: Omit>): Promise { - try { - userTasksModel.SetOrm(orm); - await userTasksModel.Update({ - where: { - id: taskId - }, - data: updates - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in updateTask: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to update task: ${error instanceof Error ? error.message : String(error)}` - }; - } +export async function updateTask( + orm: D1Orm, + taskId: number, + updates: Partial> +): Promise { + try { + userTasksModel.SetOrm(orm); + await userTasksModel.Update({ + where: { + id: taskId, + }, + data: updates, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in updateTask: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to update task: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -134,23 +138,23 @@ export async function updateTask(orm: D1Orm, taskId: number, updates: Partial { - try { - userTasksModel.SetOrm(orm); - await userTasksModel.Delete({ - where: { - id: taskId - } - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in deleteTask: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to delete task: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userTasksModel.SetOrm(orm); + await userTasksModel.Delete({ + where: { + id: taskId, + }, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in deleteTask: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to delete task: ${error instanceof Error ? error.message : String(error)}`, + }; + } } /** @@ -161,21 +165,21 @@ export async function deleteTask(orm: D1Orm, taskId: number): Promise { - try { - userTasksModel.SetOrm(orm); - await userTasksModel.Delete({ - where: { - agent_id: agentId - } - }); - return { - success: true - }; - } catch (error) { - console.error(`Error in deleteTasks: ${error instanceof Error ? error.message : String(error)}`); - return { - success: false, - error: `Failed to delete tasks: ${error instanceof Error ? error.message : String(error)}` - }; - } + try { + userTasksModel.SetOrm(orm); + await userTasksModel.Delete({ + where: { + agent_id: agentId, + }, + }); + return { + success: true, + }; + } catch (error) { + console.error(`Error in deleteTasks: ${error instanceof Error ? error.message : String(error)}`); + return { + success: false, + error: `Failed to delete tasks: ${error instanceof Error ? error.message : String(error)}`, + }; + } } diff --git a/src/database/helpers/twitter.ts b/src/database/helpers/twitter.ts index 2ba85bd..89a9f84 100644 --- a/src/database/helpers/twitter.ts +++ b/src/database/helpers/twitter.ts @@ -4,133 +4,133 @@ import { xBotAuthorsModel, xBotThreadsModel, xBotTweetsModel, xBotLogsModel } fr /** AUTHOR MANAGEMENT */ export async function getAuthor(orm: D1Orm, authorId: string) { - xBotAuthorsModel.SetOrm(orm); - const author = await xBotAuthorsModel.First({ - where: { - author_id: authorId, - }, - }); - return author; + xBotAuthorsModel.SetOrm(orm); + const author = await xBotAuthorsModel.First({ + where: { + author_id: authorId, + }, + }); + return author; } export async function addAuthor(orm: D1Orm, authorId: string, realname?: string, username?: string) { - xBotAuthorsModel.SetOrm(orm); - const author = await xBotAuthorsModel.InsertOne({ - author_id: authorId, - realname, - username, - }); - return author; + xBotAuthorsModel.SetOrm(orm); + const author = await xBotAuthorsModel.InsertOne({ + author_id: authorId, + realname, + username, + }); + return author; } /** THREAD MANAGEMENT */ export async function createThread(orm: D1Orm) { - xBotThreadsModel.SetOrm(orm); - const thread = await xBotThreadsModel.InsertOne({}); - return thread; + xBotThreadsModel.SetOrm(orm); + const thread = await xBotThreadsModel.InsertOne({}); + return thread; } export async function getThread(orm: D1Orm, threadId: number) { - xBotThreadsModel.SetOrm(orm); - const thread = await xBotThreadsModel.First({ - where: { - id: threadId, - }, - }); - return thread; + xBotThreadsModel.SetOrm(orm); + const thread = await xBotThreadsModel.First({ + where: { + id: threadId, + }, + }); + return thread; } /** TWEET MANAGEMENT */ export async function addTweet( - orm: D1Orm, - authorId: string, - tweetId: string, - tweetBody: string, - threadId?: number, - parentTweetId?: string, - isBotResponse: number = 0 + orm: D1Orm, + authorId: string, + tweetId: string, + tweetBody: string, + threadId?: number, + parentTweetId?: string, + isBotResponse: number = 0 ) { - xBotTweetsModel.SetOrm(orm); - const tweet = await xBotTweetsModel.InsertOne({ - author_id: authorId, - thread_id: threadId, - parent_tweet_id: parentTweetId, - tweet_id: tweetId, - tweet_body: tweetBody, - tweet_created_at: new Date().toISOString(), - is_bot_response: isBotResponse, - }); - return tweet; + xBotTweetsModel.SetOrm(orm); + const tweet = await xBotTweetsModel.InsertOne({ + author_id: authorId, + thread_id: threadId, + parent_tweet_id: parentTweetId, + tweet_id: tweetId, + tweet_body: tweetBody, + tweet_created_at: new Date().toISOString(), + is_bot_response: isBotResponse, + }); + return tweet; } export async function getTweet(orm: D1Orm, tweetId: string) { - xBotTweetsModel.SetOrm(orm); - const tweet = await xBotTweetsModel.First({ - where: { - tweet_id: tweetId, - }, - }); - return tweet; + xBotTweetsModel.SetOrm(orm); + const tweet = await xBotTweetsModel.First({ + where: { + tweet_id: tweetId, + }, + }); + return tweet; } export async function getThreadTweets(orm: D1Orm, threadId: number) { - xBotTweetsModel.SetOrm(orm); - const tweets = await xBotTweetsModel.All({ - where: { - thread_id: threadId, - }, - orderBy: [ - { - column: 'created_at', - descending: false, - }, - ], - }); - return tweets; + xBotTweetsModel.SetOrm(orm); + const tweets = await xBotTweetsModel.All({ + where: { + thread_id: threadId, + }, + orderBy: [ + { + column: 'created_at', + descending: false, + }, + ], + }); + return tweets; } export async function getAuthorTweets(orm: D1Orm, authorId: string) { - xBotTweetsModel.SetOrm(orm); - const tweets = await xBotTweetsModel.All({ - where: { - author_id: authorId, - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); - return tweets; + xBotTweetsModel.SetOrm(orm); + const tweets = await xBotTweetsModel.All({ + where: { + author_id: authorId, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); + return tweets; } /** LOG MANAGEMENT */ export async function addLog(orm: D1Orm, tweetId: string, status: string, message?: string) { - xBotLogsModel.SetOrm(orm); - const log = await xBotLogsModel.InsertOne({ - tweet_id: tweetId, - tweet_status: status, - log_message: message, - }); - return log; + xBotLogsModel.SetOrm(orm); + const log = await xBotLogsModel.InsertOne({ + tweet_id: tweetId, + tweet_status: status, + log_message: message, + }); + return log; } export async function getTweetLogs(orm: D1Orm, tweetId: string) { - xBotLogsModel.SetOrm(orm); - const logs = await xBotLogsModel.All({ - where: { - tweet_id: tweetId, - }, - orderBy: [ - { - column: 'created_at', - descending: true, - }, - ], - }); - return logs; + xBotLogsModel.SetOrm(orm); + const logs = await xBotLogsModel.All({ + where: { + tweet_id: tweetId, + }, + orderBy: [ + { + column: 'created_at', + descending: true, + }, + ], + }); + return logs; } From de0d137da0ad25c9b7957ee9f80722771dced015 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 09:25:30 -0700 Subject: [PATCH 074/128] feat: Add request validation and standardization utilities --- docs/request-standardization.md | 146 ++++++++++++++++++++++++++++++++ src/utils/requests-responses.ts | 40 +++++++++ 2 files changed, 186 insertions(+) create mode 100644 docs/request-standardization.md diff --git a/docs/request-standardization.md b/docs/request-standardization.md new file mode 100644 index 0000000..fd9e70f --- /dev/null +++ b/docs/request-standardization.md @@ -0,0 +1,146 @@ +# Request Standardization Guide + +## Overview + +This guide outlines our standardized approach to handling API requests, validations, and responses across all endpoints. + +## Request Validation Utilities + +We provide several utility functions in `src/utils/requests-responses.ts` to standardize request validation: + +```typescript +// Validate required URL parameters +const validation = validateRequiredParams({ id: url.searchParams.get('id') }, ['id']); +if (!validation.isValid) { + return createApiResponse(validation.error!, 400); +} + +// Validate HTTP method +const methodValidation = validateMethod(request, ['POST']); +if (!methodValidation.isValid) { + return createApiResponse(methodValidation.error!, 405); +} + +// Validate request body +const bodyValidation = validateRequiredBody(body, ['profile_id', 'crew_id']); +if (!bodyValidation.isValid) { + return createApiResponse(bodyValidation.error!, 400); +} +``` + +## Standard Response Format + +All API responses follow this structure: + +```typescript +{ + success: boolean; + data?: { + message: string; + [key: string]: any; + }; + error?: string; +} +``` + +### Success Response Example +```json +{ + "success": true, + "data": { + "message": "Successfully retrieved user profile", + "profile": { + "id": 1, + "name": "John Doe" + } + } +} +``` + +### Error Response Example +```json +{ + "success": false, + "error": "Missing required parameters: id" +} +``` + +## Implementation Guide + +1. **URL Parameters** + - Use `validateRequiredParams` for all required URL parameters + - Check validation before processing the request + - Return 400 status code for missing parameters + +2. **HTTP Methods** + - Use `validateMethod` to verify allowed HTTP methods + - Return 405 status code for unsupported methods + - Include allowed methods in error message + +3. **Request Bodies** + - Use `validateRequiredBody` for POST/PUT requests + - Validate before processing the request + - Return 400 status code for missing fields + +4. **Response Creation** + - Always use `createApiResponse` to ensure consistent format + - Include descriptive messages for both success and error cases + - Use standard HTTP status codes + +## Example Handler Implementation + +```typescript +export const handleEndpoint: Handler = async ({ orm, env, request, url }) => { + // 1. Validate HTTP method + const methodValidation = validateMethod(request, ['POST']); + if (!methodValidation.isValid) { + return createApiResponse(methodValidation.error!, 405); + } + + // 2. Validate URL parameters + const params = { + id: url.searchParams.get('id'), + type: url.searchParams.get('type') + }; + const paramValidation = validateRequiredParams(params, ['id']); + if (!paramValidation.isValid) { + return createApiResponse(paramValidation.error!, 400); + } + + // 3. Validate request body + const body = await request.json(); + const bodyValidation = validateRequiredBody(body, ['name', 'description']); + if (!bodyValidation.isValid) { + return createApiResponse(bodyValidation.error!, 400); + } + + // 4. Process request and return response + try { + const result = await processRequest(orm, params.id!, body); + return createApiResponse({ + message: 'Successfully processed request', + data: { result } + }); + } catch (error) { + return createApiResponse(error.message, 500); + } +}; +``` + +## Common Status Codes + +- 200: Successful request +- 400: Bad request (missing parameters/fields) +- 401: Unauthorized +- 403: Forbidden +- 404: Resource not found +- 405: Method not allowed +- 500: Internal server error + +## Migration Steps + +1. Update existing handlers to use new validation utilities +2. Standardize error messages and status codes +3. Ensure all responses use createApiResponse +4. Update tests to verify standard format +5. Document any endpoint-specific requirements diff --git a/src/utils/requests-responses.ts b/src/utils/requests-responses.ts index 2672561..c9ee0eb 100644 --- a/src/utils/requests-responses.ts +++ b/src/utils/requests-responses.ts @@ -4,6 +4,46 @@ type ApiResponse = { error?: string; }; +type ValidationResult = { + isValid: boolean; + error?: string; +}; + +export const standardErrors = { + METHOD_NOT_ALLOWED: (method: string) => `Method ${method} not allowed`, + MISSING_PARAMS: (params: string[]) => `Missing required parameters: ${params.join(', ')}`, + MISSING_FIELDS: (fields: string[]) => `Missing required fields: ${fields.join(', ')}`, + UNAUTHORIZED: 'Unauthorized access', + NOT_FOUND: (resource: string) => `${resource} not found`, + INVALID_INPUT: (details: string) => `Invalid input: ${details}`, +} as const; + +export function validateRequiredParams(params: Record, required: string[]): ValidationResult { + const missing = required.filter(param => !params[param]); + return { + isValid: missing.length === 0, + error: missing.length ? standardErrors.MISSING_PARAMS(missing) : undefined + }; +} + +export function validateRequiredBody>( + body: T, + required: (keyof T)[] +): ValidationResult { + const missing = required.filter(field => !body[field]); + return { + isValid: missing.length === 0, + error: missing.length ? standardErrors.MISSING_FIELDS(missing as string[]) : undefined + }; +} + +export function validateMethod(request: Request, allowed: string[]): ValidationResult { + return { + isValid: allowed.includes(request.method), + error: !allowed.includes(request.method) ? standardErrors.METHOD_NOT_ALLOWED(request.method) : undefined + }; +} + export function corsHeaders(origin?: string): HeadersInit { return { 'Access-Control-Allow-Origin': origin || '*', From 1ffafdd08e7ba13fb245a5e55b58c618f762d6fb Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 09:33:53 -0700 Subject: [PATCH 075/128] fix: apply try/catch to fetch in index This should prevent the Cloudflare 11xx errors when an exception happens and correctly capture it instead. --- src/index.ts | 128 ++++++++++++++++++++++++++------------------------- 1 file changed, 66 insertions(+), 62 deletions(-) diff --git a/src/index.ts b/src/index.ts index e53d609..9050ebd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,79 +23,83 @@ export default { * @returns The response to be sent back to the client */ async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { - // Handle CORS preflight requests - if (request.method === 'OPTIONS') { - return new Response(null, { - headers: corsHeaders(request.headers.get('Origin') || undefined), - }); - } + try { + // Handle CORS preflight requests + if (request.method === 'OPTIONS') { + return new Response(null, { + headers: corsHeaders(request.headers.get('Origin') || undefined), + }); + } - // Initialize config with environment - const config = AppConfig.getInstance(env).getConfig(); - const url = new URL(request.url); - const path = url.pathname; + // Initialize config with environment + const config = AppConfig.getInstance(env).getConfig(); + const url = new URL(request.url); + const path = url.pathname; - // Handle root path - if (path === '' || path === '/') { - return createApiResponse({ - message: 'aibtc.dev services online', - data: { - endpoints: config.SUPPORTED_SERVICES, - }, - }); - } + // Handle root path + if (path === '' || path === '/') { + return createApiResponse({ + message: 'aibtc.dev services online', + data: { + endpoints: config.SUPPORTED_SERVICES, + }, + }); + } - // For the Durable Object responses, the CORS headers will be added by the DO handlers + // For the Durable Object responses, the CORS headers will be added by the DO handlers - if (path.startsWith('/auth')) { - const id: DurableObjectId = env.AUTH_DO.idFromName('auth-do'); // create the instance - const stub = env.AUTH_DO.get(id); // get the stub for communication - return await stub.fetch(request); // forward the request to the Durable Object - } + if (path.startsWith('/auth')) { + const id: DurableObjectId = env.AUTH_DO.idFromName('auth-do'); // create the instance + const stub = env.AUTH_DO.get(id); // get the stub for communication + return await stub.fetch(request); // forward the request to the Durable Object + } - if (path.startsWith('/cdn')) { - let id: DurableObjectId = env.CDN_DO.idFromName('cdn-do'); // create the instance - let stub = env.CDN_DO.get(id); // get the stub for communication - return await stub.fetch(request); // forward the request to the Durable Object - } + if (path.startsWith('/cdn')) { + let id: DurableObjectId = env.CDN_DO.idFromName('cdn-do'); // create the instance + let stub = env.CDN_DO.get(id); // get the stub for communication + return await stub.fetch(request); // forward the request to the Durable Object + } - if (path.startsWith('/context')) { - const id: DurableObjectId = env.CONTEXT_DO.idFromName('context-do'); // create the instance - const stub = env.CONTEXT_DO.get(id); // get the stub for communication - return await stub.fetch(request); // forward the request to the Durable Object - } + if (path.startsWith('/context')) { + const id: DurableObjectId = env.CONTEXT_DO.idFromName('context-do'); // create the instance + const stub = env.CONTEXT_DO.get(id); // get the stub for communication + return await stub.fetch(request); // forward the request to the Durable Object + } - if (path.startsWith('/database')) { - const id: DurableObjectId = env.DATABASE_DO.idFromName('database-do'); // create the instance - const stub = env.DATABASE_DO.get(id); // get the stub for communication - return await stub.fetch(request); // forward the request to the Durable Object - } + if (path.startsWith('/database')) { + const id: DurableObjectId = env.DATABASE_DO.idFromName('database-do'); // create the instance + const stub = env.DATABASE_DO.get(id); // get the stub for communication + return await stub.fetch(request); // forward the request to the Durable Object + } - if (path.startsWith('/image-generator')) { - let id: DurableObjectId = env.IMAGE_GENERATOR_DO.idFromName('image-generator-do'); // create the instance - let stub = env.IMAGE_GENERATOR_DO.get(id); // get the stub for communication - return await stub.fetch(request); // forward the request to the Durable Object - } + if (path.startsWith('/image-generator')) { + let id: DurableObjectId = env.IMAGE_GENERATOR_DO.idFromName('image-generator-do'); // create the instance + let stub = env.IMAGE_GENERATOR_DO.get(id); // get the stub for communication + return await stub.fetch(request); // forward the request to the Durable Object + } - if (path.startsWith('/metadata-generator')) { - let id: DurableObjectId = env.METADATA_GENERATOR_DO.idFromName('metadata-generator-do'); // create the instance - let stub = env.METADATA_GENERATOR_DO.get(id); // get the stub for communication - return await stub.fetch(request); // forward the request to the Durable Object - } + if (path.startsWith('/metadata-generator')) { + let id: DurableObjectId = env.METADATA_GENERATOR_DO.idFromName('metadata-generator-do'); // create the instance + let stub = env.METADATA_GENERATOR_DO.get(id); // get the stub for communication + return await stub.fetch(request); // forward the request to the Durable Object + } - if (path.startsWith('/scheduler')) { - let id: DurableObjectId = env.SCHEDULER_DO.idFromName('scheduler-do'); // create the instance - let stub = env.SCHEDULER_DO.get(id); // get the stub for communication - return await stub.fetch(request); // forward the request to the Durable Object - } + if (path.startsWith('/scheduler')) { + let id: DurableObjectId = env.SCHEDULER_DO.idFromName('scheduler-do'); // create the instance + let stub = env.SCHEDULER_DO.get(id); // get the stub for communication + return await stub.fetch(request); // forward the request to the Durable Object + } - if (path.startsWith('/tools')) { - let id: DurableObjectId = env.TOOLS_DO.idFromName('tools-do'); // create the instance - let stub = env.TOOLS_DO.get(id); // get the stub for communication - return await stub.fetch(request); // forward the request to the Durable Object - } + if (path.startsWith('/tools')) { + let id: DurableObjectId = env.TOOLS_DO.idFromName('tools-do'); // create the instance + let stub = env.TOOLS_DO.get(id); // get the stub for communication + return await stub.fetch(request); // forward the request to the Durable Object + } - // Return 404 for any other path - return createUnsupportedEndpointResponse(path, config.SUPPORTED_SERVICES); + // Return 404 for any other path + return createUnsupportedEndpointResponse(path, config.SUPPORTED_SERVICES); + } catch (error) { + return createApiResponse(`Unknown error: ${error instanceof Error ? error.message : String(error)}`, 500); + } }, } satisfies ExportedHandler; From 7c1b84661b5f7a4eac4a117a83ec92408e780d47 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Fri, 20 Dec 2024 09:44:19 -0700 Subject: [PATCH 076/128] docs: Add comprehensive authentication system improvements documentation --- docs/authentication-improvements.md | 228 ++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 docs/authentication-improvements.md diff --git a/docs/authentication-improvements.md b/docs/authentication-improvements.md new file mode 100644 index 0000000..dffe6fa --- /dev/null +++ b/docs/authentication-improvements.md @@ -0,0 +1,228 @@ +# Authentication System Improvements + +## Overview + +This document outlines improvements to our authentication system while maintaining simplicity and security. The changes focus on standardizing token handling, improving security, and following best practices. + +## Key Improvements + +1. Bearer Token Format +2. HTTP-Only Secure Cookies +3. Token Rotation (Access + Refresh Tokens) +4. Rate Limiting +5. CSRF Protection + +## Implementation Details + +### 1. Bearer Token Format + +Update the Authorization header handling to use the standard Bearer format: + +```typescript +// Before +Authorization: your-token-here + +// After +Authorization: Bearer your-token-here +``` + +### 2. Token Structure + +Implement a two-token system: +- Access Token: Short-lived (1 hour) +- Refresh Token: Long-lived (30 days) + +```typescript +interface TokenPair { + accessToken: string; // 1 hour lifetime + refreshToken: string; // 30 days lifetime +} + +interface TokenMetadata { + address: string; + type: 'access' | 'refresh'; + exp: number; +} +``` + +### 3. Token Storage + +Store tokens in KV with appropriate TTL: + +```typescript +// Access token (1 hour) +await kv.put(`auth:access:${accessToken}`, JSON.stringify({ + address, + type: 'access', + exp: Date.now() + 3600000 +}), { expirationTtl: 3600 }); + +// Refresh token (30 days) +await kv.put(`auth:refresh:${refreshToken}`, JSON.stringify({ + address, + type: 'refresh', + exp: Date.now() + 2592000000 +}), { expirationTtl: 2592000 }); +``` + +### 4. Cookie Security + +Set secure HTTP-only cookies for both tokens: + +```typescript +const setCookieHeaders = { + 'Set-Cookie': [ + `access_token=${accessToken}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`, + `refresh_token=${refreshToken}; HttpOnly; Secure; SameSite=Strict; Max-Age=2592000` + ] +}; +``` + +### 5. Rate Limiting + +Implement rate limiting using DurableObject storage: + +```typescript +const RATE_LIMIT = { + WINDOW_MS: 3600000, // 1 hour + MAX_ATTEMPTS: 5 +}; + +async function checkRateLimit(address: string): Promise { + const key = `ratelimit:auth:${address}`; + const attempts = await this.storage.get(key) || 0; + + if (attempts >= RATE_LIMIT.MAX_ATTEMPTS) { + return false; + } + + await this.storage.put(key, attempts + 1, { + expiration: Date.now() + RATE_LIMIT.WINDOW_MS + }); + + return true; +} +``` + +### 6. CSRF Protection + +Add CSRF token validation for sensitive operations: + +```typescript +const csrfToken = crypto.randomUUID(); +await kv.put(`csrf:${csrfToken}`, address, { + expirationTtl: 3600 +}); + +// Include in response headers +headers['X-CSRF-Token'] = csrfToken; +``` + +## API Endpoints + +### 1. Request Auth Token + +```typescript +POST /auth/request-auth-token +Content-Type: application/json +Authorization: Bearer + +{ + "signature": "...", + "publicKey": "..." +} + +Response: +{ + "success": true, + "data": { + "message": "Authentication successful", + "address": "ST..." + } +} ++ Set-Cookie headers +``` + +### 2. Refresh Token + +```typescript +POST /auth/refresh +Cookie: refresh_token= + +Response: +{ + "success": true, + "data": { + "message": "Token refreshed" + } +} ++ New Set-Cookie headers +``` + +### 3. Verify Token + +```typescript +POST /auth/verify +Cookie: access_token= + +Response: +{ + "success": true, + "data": { + "address": "ST...", + "exp": 1234567890 + } +} +``` + +### 4. Revoke Token + +```typescript +POST /auth/revoke +Cookie: access_token= +X-CSRF-Token: + +Response: +{ + "success": true, + "data": { + "message": "Tokens revoked" + } +} +``` + +## Migration Steps + +1. Update `validateSharedKeyAuth` to handle Bearer format +2. Implement token rotation system +3. Add cookie-based token storage +4. Add rate limiting +5. Add CSRF protection +6. Update existing endpoints to use new token system +7. Add new endpoints (refresh, revoke) + +## Security Considerations + +1. Always use HTTPS +2. Set appropriate cookie security flags +3. Implement proper error handling +4. Log security-relevant events +5. Regular token cleanup +6. Monitor for suspicious activity + +## Testing + +1. Test token rotation +2. Verify rate limiting +3. Test CSRF protection +4. Verify cookie security +5. Test token revocation +6. Test error scenarios + +## Future Improvements + +1. Add token blacklisting +2. Implement device fingerprinting +3. Add audit logging +4. Add automated security monitoring +5. Implement progressive security measures From eccc5667bc0bcfd930e5d9955400fabe35593cce Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 09:59:21 -0700 Subject: [PATCH 077/128] fix: remove unused alarms, documented format in #35 --- src/auth/auth-do.ts | 16 +--------------- src/cdn/cdn-do.ts | 16 +--------------- src/context/context-do.ts | 16 +--------------- src/database/database-do.ts | 16 +--------------- src/index.ts | 7 ++++++- src/scheduler/scheduler-do.ts | 16 +--------------- src/tools/tools-do.ts | 16 +--------------- 7 files changed, 12 insertions(+), 91 deletions(-) diff --git a/src/auth/auth-do.ts b/src/auth/auth-do.ts index 525d620..d94cd7d 100644 --- a/src/auth/auth-do.ts +++ b/src/auth/auth-do.ts @@ -26,21 +26,7 @@ export class AuthDO extends DurableObject { this.ALARM_INTERVAL_MS = config.ALARM_INTERVAL_MS; // Set up alarm to run at configured interval - ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); - } - - async alarm(): Promise { - try { - console.log(`AuthDO: alarm activated`); - } catch (error) { - console.error(`AuthDO: alarm execution failed: ${error instanceof Error ? error.message : String(error)}`); - } finally { - // Always schedule next alarm if one isn't set - const currentAlarm = await this.ctx.storage.getAlarm(); - if (currentAlarm === null) { - this.ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); - } - } + // ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); } async fetch(request: Request): Promise { diff --git a/src/cdn/cdn-do.ts b/src/cdn/cdn-do.ts index 55c9b89..18c8598 100644 --- a/src/cdn/cdn-do.ts +++ b/src/cdn/cdn-do.ts @@ -20,21 +20,7 @@ export class CdnDO extends DurableObject { this.ALARM_INTERVAL_MS = config.ALARM_INTERVAL_MS; // Set up alarm to run at configured interval - ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); - } - - async alarm(): Promise { - try { - console.log(`CdnDO: alarm activated`); - } catch (error) { - console.error(`CdnDO: alarm execution failed: ${error instanceof Error ? error.message : String(error)}`); - } finally { - // Always schedule next alarm if one isn't set - const currentAlarm = await this.ctx.storage.getAlarm(); - if (currentAlarm === null) { - this.ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); - } - } + // ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); } async fetch(request: Request): Promise { diff --git a/src/context/context-do.ts b/src/context/context-do.ts index 2687661..2ed0ecd 100644 --- a/src/context/context-do.ts +++ b/src/context/context-do.ts @@ -21,21 +21,7 @@ export class ContextDO extends DurableObject { this.ALARM_INTERVAL_MS = config.ALARM_INTERVAL_MS; // Set up alarm to run at configured interval - ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); - } - - async alarm(): Promise { - try { - console.log(`ContextDO: alarm activated`); - } catch (error) { - console.error(`ContextDO: alarm execution failed: ${error instanceof Error ? error.message : String(error)}`); - } finally { - // Always schedule next alarm if one isn't set - const currentAlarm = await this.ctx.storage.getAlarm(); - if (currentAlarm === null) { - this.ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); - } - } + // ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); } async fetch(request: Request): Promise { diff --git a/src/database/database-do.ts b/src/database/database-do.ts index 518f7c4..c8e4fb1 100644 --- a/src/database/database-do.ts +++ b/src/database/database-do.ts @@ -76,21 +76,7 @@ export class DatabaseDO extends DurableObject { this.ALARM_INTERVAL_MS = config.ALARM_INTERVAL_MS; // Set up alarm to run at configured interval - ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); - } - - async alarm(): Promise { - try { - console.log(`DatabaseDO: alarm activated`); - } catch (error) { - console.error(`DatabaseDO: alarm execution failed: ${error instanceof Error ? error.message : String(error)}`); - } finally { - // Always schedule next alarm if one isn't set - const currentAlarm = await this.ctx.storage.getAlarm(); - if (currentAlarm === null) { - this.ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); - } - } + // ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); } async fetch(request: Request): Promise { diff --git a/src/index.ts b/src/index.ts index 9050ebd..498724f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,7 +46,11 @@ export default { }); } - // For the Durable Object responses, the CORS headers will be added by the DO handlers + // Each service is represented by its own Durable Object + // - services are grouped by folder in the src directory + // - each service has its own reources and logic + // - we pass the request to the DO and it constructs the response + // - response headers and formatting are handled by shared utils if (path.startsWith('/auth')) { const id: DurableObjectId = env.AUTH_DO.idFromName('auth-do'); // create the instance @@ -99,6 +103,7 @@ export default { // Return 404 for any other path return createUnsupportedEndpointResponse(path, config.SUPPORTED_SERVICES); } catch (error) { + // Return 500 for any unhandled error return createApiResponse(`Unknown error: ${error instanceof Error ? error.message : String(error)}`, 500); } }, diff --git a/src/scheduler/scheduler-do.ts b/src/scheduler/scheduler-do.ts index 38d1771..f0b160b 100644 --- a/src/scheduler/scheduler-do.ts +++ b/src/scheduler/scheduler-do.ts @@ -21,21 +21,7 @@ export class SchedulerDO extends DurableObject { this.ALARM_INTERVAL_MS = config.ALARM_INTERVAL_MS; // Set up alarm to run at configured interval - ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); - } - - async alarm(): Promise { - try { - console.log(`SchedulerDO: alarm activated`); - } catch (error) { - console.error(`SchedulerDO: alarm execution failed: ${error instanceof Error ? error.message : String(error)}`); - } finally { - // Always schedule next alarm if one isn't set - const currentAlarm = await this.ctx.storage.getAlarm(); - if (currentAlarm === null) { - this.ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); - } - } + // ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); } async fetch(request: Request): Promise { diff --git a/src/tools/tools-do.ts b/src/tools/tools-do.ts index 4912bf8..d002b10 100644 --- a/src/tools/tools-do.ts +++ b/src/tools/tools-do.ts @@ -21,21 +21,7 @@ export class ToolsDO extends DurableObject { this.ALARM_INTERVAL_MS = config.ALARM_INTERVAL_MS; // Set up alarm to run at configured interval - ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); - } - - async alarm(): Promise { - try { - console.log(`ToolsDO: alarm activated`); - } catch (error) { - console.error(`ToolsDO: alarm execution failed: ${error instanceof Error ? error.message : String(error)}`); - } finally { - // Always schedule next alarm if one isn't set - const currentAlarm = await this.ctx.storage.getAlarm(); - if (currentAlarm === null) { - this.ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); - } - } + // ctx.storage.setAlarm(Date.now() + this.ALARM_INTERVAL_MS); } async fetch(request: Request): Promise { From 553bb4a9bbc6936ab163e67ea285331cf0e3ce6d Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 10:05:21 -0700 Subject: [PATCH 078/128] fix: use stx_address in place of profile_id --- src/database/database-schema.sql | 52 ++++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/database/database-schema.sql b/src/database/database-schema.sql index 5a7498c..6dfd6f6 100644 --- a/src/database/database-schema.sql +++ b/src/database/database-schema.sql @@ -7,7 +7,7 @@ -- ============================================================================ -- Basic Configuration -- ============================================================================ --- All tables link back to user_profiles with stx_address as profile_id. +-- All tables link back to user_profiles with stx_address. -- All tables contain an auto-incrementing id, created_at, and updated_at field. -- All tables have indexes for commonly looked up columns. -- All tables have a trigger to update the updated_at field. @@ -58,17 +58,17 @@ CREATE TABLE user_socials ( created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, -- updated by trigger - profile_id TEXT NOT NULL, + stx_address TEXT NOT NULL, -- link to user_profiles platform TEXT NOT NULL, -- Twitter, Telegram, Discord, etc platform_id TEXT NOT NULL, -- ID on the platform - FOREIGN KEY (profile_id) REFERENCES user_profiles(stx_address) ON DELETE CASCADE + FOREIGN KEY (stx_address) REFERENCES user_profiles(stx_address) ON DELETE CASCADE ); -- Define indexes -CREATE INDEX idx_socials_profile_id ON user_socials(profile_id); +CREATE INDEX idx_socials_stx_address ON user_socials(stx_address); CREATE INDEX idx_socials_platform ON user_socials(platform); CREATE INDEX idx_socials_platform_id ON user_socials(platform_id); @@ -93,7 +93,7 @@ CREATE TABLE user_crews ( created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, -- updated by trigger - profile_id TEXT NOT NULL, + stx_address TEXT NOT NULL, -- link to user_profiles crew_name TEXT NOT NULL, crew_description TEXT, @@ -102,11 +102,11 @@ CREATE TABLE user_crews ( crew_is_public INTEGER DEFAULT 0, crew_is_cron INTEGER DEFAULT 0, -- synced with user_crons.cron_enabled - FOREIGN KEY (profile_id) REFERENCES user_profiles(stx_address) ON DELETE CASCADE + FOREIGN KEY (stx_address) REFERENCES user_profiles(stx_address) ON DELETE CASCADE ); -- Define indexes -CREATE INDEX idx_crews_profile_id ON user_crews(profile_id); +CREATE INDEX idx_crews_stx_address ON user_crews(stx_address); CREATE INDEX idx_crews_crew_name ON user_crews(crew_name); -- Trigger for user_crews @@ -129,7 +129,7 @@ CREATE TABLE user_agents ( created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, -- updated by trigger - profile_id TEXT NOT NULL, + stx_address TEXT NOT NULL, -- link to user_profiles crew_id INTEGER NOT NULL, -- link to user_crews @@ -138,12 +138,12 @@ CREATE TABLE user_agents ( agent_goal TEXT NOT NULL, agent_backstory TEXT NOT NULL, agent_tools TEXT, - FOREIGN KEY (profile_id) REFERENCES user_profiles(stx_address) ON DELETE CASCADE, + FOREIGN KEY (stx_address) REFERENCES user_profiles(stx_address) ON DELETE CASCADE, FOREIGN KEY (crew_id) REFERENCES user_crews(id) ON DELETE CASCADE ); -- Define indexes -CREATE INDEX idx_agents_profile_id ON user_agents(profile_id); +CREATE INDEX idx_agents_stx_address ON user_agents(stx_address); CREATE INDEX idx_agents_crew_id ON user_agents(crew_id); -- Trigger for user_agents @@ -166,7 +166,7 @@ CREATE TABLE user_tasks ( created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, -- updated by trigger - profile_id TEXT NOT NULL, + stx_address TEXT NOT NULL, -- link to user_profiles crew_id INTEGER NOT NULL, -- link to user_crews @@ -175,13 +175,13 @@ CREATE TABLE user_tasks ( task_name TEXT NOT NULL, task_description TEXT NOT NULL, task_expected_output TEXT NOT NULL, - FOREIGN KEY (profile_id) REFERENCES user_profiles(stx_address) ON DELETE CASCADE, + FOREIGN KEY (stx_address) REFERENCES user_profiles(stx_address) ON DELETE CASCADE, FOREIGN KEY (crew_id) REFERENCES user_crews(id) ON DELETE CASCADE, FOREIGN KEY (agent_id) REFERENCES user_agents(id) ON DELETE CASCADE ); -- Define indexes -CREATE INDEX idx_tasks_profile_id ON user_tasks(profile_id); +CREATE INDEX idx_tasks_stx_address ON user_tasks(stx_address); CREATE INDEX idx_tasks_crew_id ON user_tasks(crew_id); CREATE INDEX idx_tasks_agent_id ON user_tasks(agent_id); @@ -205,15 +205,15 @@ CREATE TABLE user_conversations ( created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, -- updated by trigger - profile_id TEXT NOT NULL, + stx_address TEXT NOT NULL, -- link to user_profiles conversation_name TEXT NOT NULL, - FOREIGN KEY (profile_id) REFERENCES user_profiles(stx_address) ON DELETE CASCADE + FOREIGN KEY (stx_address) REFERENCES user_profiles(stx_address) ON DELETE CASCADE ); -- Define indexes CREATE INDEX idx_conversations_created_at ON user_conversations(created_at); -CREATE INDEX idx_conversations_profile_id ON user_conversations(profile_id); +CREATE INDEX idx_conversations_stx_address ON user_conversations(stx_address); -- Trigger for user_conversations CREATE TRIGGER update_conversations_timestamp @@ -234,7 +234,7 @@ CREATE TABLE user_crew_executions ( id INTEGER PRIMARY KEY AUTOINCREMENT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, - profile_id TEXT NOT NULL, + stx_address TEXT NOT NULL, -- link to user_profiles crew_id INTEGER NOT NULL, -- link to user_crews @@ -244,14 +244,14 @@ CREATE TABLE user_crew_executions ( final_result TEXT, total_tokens INTEGER, successful_requests INTEGER, - FOREIGN KEY (profile_id) REFERENCES user_profiles(stx_address) ON DELETE CASCADE, + FOREIGN KEY (stx_address) REFERENCES user_profiles(stx_address) ON DELETE CASCADE, FOREIGN KEY (crew_id) REFERENCES user_crews(id) ON DELETE CASCADE, FOREIGN KEY (conversation_id) REFERENCES user_conversations(id) ON DELETE CASCADE ); -- Define indexes CREATE INDEX idx_executions_created_at ON user_crew_executions(created_at); -CREATE INDEX idx_executions_profile_id ON user_crew_executions(profile_id); +CREATE INDEX idx_executions_stx_address ON user_crew_executions(stx_address); CREATE INDEX idx_executions_crew_id ON user_crew_executions(crew_id); CREATE INDEX idx_executions_conversation_id ON user_crew_executions(conversation_id); @@ -284,7 +284,7 @@ CREATE TABLE user_crew_execution_steps ( id INTEGER PRIMARY KEY AUTOINCREMENT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, - profile_id TEXT NOT NULL, + stx_address TEXT NOT NULL, -- link to user_profiles crew_id INTEGER NOT NULL, -- link to user_crews @@ -294,16 +294,16 @@ CREATE TABLE user_crew_execution_steps ( -- Thought, Action, Tool Output, Final Answer, etc step_data TEXT NOT NULL, -- Actual output to parse - FOREIGN KEY (profile_id) REFERENCES user_profiles(stx_address) ON DELETE CASCADE, + FOREIGN KEY (stx_address) REFERENCES user_profiles(stx_address) ON DELETE CASCADE, FOREIGN KEY (crew_id) REFERENCES user_crews(id) ON DELETE CASCADE, FOREIGN KEY (execution_id) REFERENCES user_crew_executions(id) ON DELETE CASCADE ); -- Define indexes for execution steps CREATE INDEX idx_execution_steps_created_at ON user_crew_execution_steps(created_at); -CREATE INDEX idx_execution_steps_profile_id ON user_crew_execution_steps(profile_id); +CREATE INDEX idx_execution_steps_stx_address ON user_crew_execution_steps(stx_address); CREATE INDEX idx_execution_steps_crew_id ON user_crew_execution_steps(crew_id); -CREATE INDEX idx_execution_steps_profile_id ON user_crew_execution_steps(profile_id); +CREATE INDEX idx_execution_steps_stx_address ON user_crew_execution_steps(stx_address); CREATE INDEX idx_execution_steps_execution_id ON user_crew_execution_steps(execution_id); CREATE INDEX idx_execution_steps_type ON user_crew_execution_steps(step_type); @@ -326,20 +326,20 @@ CREATE TABLE user_crons ( id INTEGER PRIMARY KEY AUTOINCREMENT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, - profile_id TEXT NOT NULL, + stx_address TEXT NOT NULL, crew_id INTEGER NOT NULL, cron_last_run DATETIME DEFAULT NULL, cron_next_run DATETIME DEFAULT NULL, cron_enabled INTEGER NOT NULL DEFAULT 0, cron_interval TEXT NOT NULL, cron_input TEXT NOT NULL, - FOREIGN KEY (profile_id) REFERENCES user_profiles(stx_address) ON DELETE CASCADE, + FOREIGN KEY (stx_address) REFERENCES user_profiles(stx_address) ON DELETE CASCADE, FOREIGN KEY (crew_id) REFERENCES user_crews(id) ON DELETE CASCADE ); -- Define indexes for crons CREATE INDEX idx_crons_created_at ON user_crons(created_at); -CREATE INDEX idx_crons_profile_id ON user_crons(profile_id); +CREATE INDEX idx_crons_stx_address ON user_crons(stx_address); CREATE INDEX idx_crons_crew_id ON user_crons(crew_id); CREATE INDEX idx_crons_enabled ON user_crons(cron_enabled); From 2d9ac66eff357cf8988b19114589cce2725dc078 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Fri, 20 Dec 2024 10:14:17 -0700 Subject: [PATCH 079/128] docs: add analysis of database schema Can use this with aider, the schema, and the analysis file to regenerate each section after any changes. --- docs/database-schema-analysis.md | 224 +++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 docs/database-schema-analysis.md diff --git a/docs/database-schema-analysis.md b/docs/database-schema-analysis.md new file mode 100644 index 0000000..f739179 --- /dev/null +++ b/docs/database-schema-analysis.md @@ -0,0 +1,224 @@ +# Database Schema Analysis for AIBTCDev + +## 1. Table Relationships + +### Primary Table + +- `user_profiles`: Core table that all other tables reference via `stx_address` + +### Direct Relationships (Parent → Child) + +1. user_profiles → user_socials + + - One-to-Many: One profile can have multiple social accounts + - Connected via `stx_address` + - CASCADE delete + +2. user_profiles → user_crews + + - One-to-Many: One profile can have multiple crews + - Connected via `stx_address` + - CASCADE delete + +3. user_crews → user_agents + + - One-to-Many: One crew can have multiple agents + - Connected via `crew_id` and `stx_address` + - CASCADE delete + +4. user_crews → user_tasks + + - One-to-Many: One crew can have multiple tasks + - Connected via `crew_id` and `stx_address` + - CASCADE delete + +5. user_profiles → user_conversations + + - One-to-Many: One profile can have multiple conversations + - Connected via `stx_address` + - CASCADE delete + +6. user_crews → user_crew_executions + + - One-to-Many: One crew can have multiple executions + - Connected via `crew_id` and `stx_address` + - CASCADE delete + +7. user_conversations → user_crew_executions + + - One-to-Many: One conversation can have multiple executions + - Connected via `conversation_id` + - CASCADE delete + +8. user_crew_executions → user_crew_execution_steps + + - One-to-Many: One execution can have multiple steps + - Connected via `execution_id`, `crew_id`, and `stx_address` + - CASCADE delete + +9. user_crews → user_crons + - One-to-One: One crew can have one cron configuration + - Connected via `crew_id` and `stx_address` + - CASCADE delete + +## 2. Indexes and Triggers + +### Common Indexes Across All Tables + +- Primary Key (`id`) +- `stx_address` +- `created_at` (on select tables) + +### Table-Specific Indexes + +1. user_profiles + + - `user_role` + - `account_index` + - `bns_address` + +2. user_socials + + - `platform` + - `platform_id` + +3. user_crews + + - `crew_name` + +4. user_crew_execution_steps + + - `step_type` + +5. user_crons + - `cron_enabled` + +### Triggers + +1. Timestamp Updates + + - All tables have an `update_*_timestamp` trigger + - Updates `updated_at` to `CURRENT_TIMESTAMP` on record modification + +2. Specialized Triggers + - `increment_execution_count`: Increments `crew_executions` in user_crews + - `sync_crew_cron_status`: Syncs `crew_is_cron` with cron_enabled + - `sync_crew_cron_status_insert`: Sets initial cron status + - `sync_crew_cron_status_delete`: Resets cron status on deletion + +## 3. Column Constraints + +### NOT NULL Constraints + +1. user_profiles + + - `user_role` + - `stx_address` (also UNIQUE) + +2. user_socials + + - `stx_address` + - `platform` + - `platform_id` + +3. user_crews + + - `stx_address` + - `crew_name` + +4. user_agents + + - `stx_address` + - `crew_id` + - `agent_name` + - `agent_role` + - `agent_goal` + - `agent_backstory` + +5. user_tasks + + - All columns except `id`, `created_at`, `updated_at` + +6. user_conversations + + - `stx_address` + - `conversation_name` + +7. user_crew_executions + + - `stx_address` + - `crew_id` + - `conversation_id` + +8. user_crew_execution_steps + + - `stx_address` + - `crew_id` + - `execution_id` + - `step_type` + - `step_data` + +9. user_crons + - `stx_address` + - `crew_id` + - `cron_enabled` + - `cron_interval` + - `cron_input` + +### Default Values + +- All tables: `created_at` and `updated_at` default to `CURRENT_TIMESTAMP` +- user_crews: `crew_executions` defaults to 0 +- user_crews: `crew_is_public` and `crew_is_cron` default to 0 +- user_crons: `cron_enabled` defaults to 0 + +## 4. Recommendations for Improvement + +### Data Integrity + +1. Consider adding CHECK constraints for: + + - `user_role` valid values + - `platform` valid values in user_socials + - `step_type` valid values in execution_steps + - `cron_interval` format validation + +2. Add constraints for numeric fields: + - `total_tokens` > 0 + - `successful_requests` >= 0 + - `crew_executions` >= 0 + +### Performance + +1. Consider composite indexes for common query patterns: + + - (stx_address, created_at) + - (crew_id, created_at) + - (execution_id, step_type) + +2. Add indexes for range queries: + - user_crons: (cron_enabled, cron_next_run) + +### Schema Design + +1. Consider splitting large text fields into separate tables: + + - agent_backstory + - task_description + - step_data + +2. Add versioning support: + - Version columns for crews, agents, and tasks + - History tables for tracking changes + +### Maintenance + +1. Add documentation tables: + + - Table descriptions + - Column descriptions + - Constraint explanations + +2. Implement cleanup procedures: + - Archival strategy for old executions + - Cleanup triggers for orphaned records From 4ae7f2f0456053aff29a3526ddb7e2fc57dd5fe0 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:09:06 -0700 Subject: [PATCH 080/128] feat: Add new handler definition types and documentation methods --- src/database/handlers/index.ts | 41 +++++++++++++++++++++++++++------- src/database/handlers/types.ts | 20 +++++++++++++---- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/database/handlers/index.ts b/src/database/handlers/index.ts index d0639e2..23898e8 100644 --- a/src/database/handlers/index.ts +++ b/src/database/handlers/index.ts @@ -1,12 +1,37 @@ -import { Handler } from './types'; -import { handleConversations } from './conversations'; +import { Handler, HandlerDefinition } from './types'; +import { profilesHandler } from './profiles'; +import { conversationsHandler } from './conversations'; +import { crewsHandler } from './crews'; +import { agentsHandler } from './agents'; +import { tasksHandler } from './tasks'; +import { cronsHandler } from './crons'; +import { twitterHandler } from './twitter'; -const handlers: Record = { - conversations: handleConversations, - // Add more handlers as needed -}; +const handlerDefinitions: HandlerDefinition[] = [ + profilesHandler, + conversationsHandler, + crewsHandler, + agentsHandler, + tasksHandler, + cronsHandler, + twitterHandler +]; export const getHandler = (path: string): Handler | undefined => { - const segment = path.split('/')[1]; - return handlers[segment]; + const segment = path.split('/')[1]; + const definition = handlerDefinitions.find(def => def.baseRoute === segment); + return definition?.handler; +}; + +export const getSupportedEndpoints = (): string[] => { + return handlerDefinitions.flatMap(def => + def.endpoints.map(endpoint => endpoint.path) + ); +}; + +export const getEndpointDocumentation = () => { + return handlerDefinitions.map(def => ({ + baseRoute: def.baseRoute, + endpoints: def.endpoints + })); }; diff --git a/src/database/handlers/types.ts b/src/database/handlers/types.ts index 66a7ef4..afdb52a 100644 --- a/src/database/handlers/types.ts +++ b/src/database/handlers/types.ts @@ -1,11 +1,23 @@ import { D1Orm } from 'd1-orm'; import { Env } from '../../../worker-configuration'; +export interface HandlerEndpoint { + path: string; + methods: string[]; + description?: string; +} + +export interface HandlerDefinition { + baseRoute: string; + endpoints: HandlerEndpoint[]; + handler: Handler; +} + export interface HandlerContext { - orm: D1Orm; - env: Env; - request: Request; - url: URL; + orm: D1Orm; + env: Env; + request: Request; + url: URL; } export type Handler = (context: HandlerContext) => Promise; From 67dab1d5843a01dc226f85a48b60672384a4e2fd Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:09:29 -0700 Subject: [PATCH 081/128] refactor: Update profiles handler to use new HandlerDefinition structure --- src/database/handlers/profiles.ts | 43 +++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/database/handlers/profiles.ts b/src/database/handlers/profiles.ts index f9f77d6..3ffd3c0 100644 --- a/src/database/handlers/profiles.ts +++ b/src/database/handlers/profiles.ts @@ -1,4 +1,4 @@ -import { Handler } from './types'; +import { Handler, HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { getUserRole, @@ -11,7 +11,46 @@ import { } from '../helpers/profiles'; import { UserProfilesTable } from '../models'; -export const handleProfiles: Handler = async ({ orm, env, request, url }) => { +export const profilesHandler: HandlerDefinition = { + baseRoute: 'profiles', + endpoints: [ + { + path: '/profiles/role', + methods: ['GET'], + description: 'Get user role by address' + }, + { + path: '/profiles/get', + methods: ['GET'], + description: 'Get user profile by address' + }, + { + path: '/profiles/create', + methods: ['POST'], + description: 'Create new user profile' + }, + { + path: '/profiles/update', + methods: ['PUT'], + description: 'Update existing user profile' + }, + { + path: '/profiles/delete', + methods: ['DELETE'], + description: 'Delete user profile' + }, + { + path: '/profiles/list', + methods: ['GET'], + description: 'Get all user profiles' + }, + { + path: '/profiles/admin-update', + methods: ['PUT'], + description: 'Admin update of user profile by ID' + } + ], + handler: async ({ orm, env, request, url }) => { const endpoint = url.pathname.split('/').pop(); switch (endpoint) { From 0a88eb4b9ac82a31e6a6f029990db4ebd679b2fd Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:09:42 -0700 Subject: [PATCH 082/128] refactor: Convert conversations handler to new format with endpoints --- src/database/handlers/conversations.ts | 28 ++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/database/handlers/conversations.ts b/src/database/handlers/conversations.ts index c6ddb21..59c0b42 100644 --- a/src/database/handlers/conversations.ts +++ b/src/database/handlers/conversations.ts @@ -1,9 +1,33 @@ -import { Handler } from './types'; +import { Handler, HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { getConversations, getLatestConversation, getConversationHistory, addConversation } from '../helpers/conversations'; import { UserConversationsTable } from '../models'; -export const handleConversations: Handler = async ({ orm, request, url }) => { +export const conversationsHandler: HandlerDefinition = { + baseRoute: 'conversations', + endpoints: [ + { + path: '/conversations/conversations', + methods: ['GET'], + description: 'Get all conversations for an address' + }, + { + path: '/conversations/latest', + methods: ['GET'], + description: 'Get latest conversation for an address' + }, + { + path: '/conversations/history', + methods: ['GET'], + description: 'Get conversation history by ID' + }, + { + path: '/conversations/create', + methods: ['POST'], + description: 'Create a new conversation' + } + ], + handler: async ({ orm, request, url }) => { const endpoint = url.pathname.split('/').pop(); switch (endpoint) { From 202f7bf0eef5431f19a38cfeb03cd00dd1216fb1 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:10:01 -0700 Subject: [PATCH 083/128] refactor: Convert agents handler to new HandlerDefinition format --- src/database/handlers/agents.ts | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/database/handlers/agents.ts b/src/database/handlers/agents.ts index 14a5878..45da72f 100644 --- a/src/database/handlers/agents.ts +++ b/src/database/handlers/agents.ts @@ -1,10 +1,34 @@ -import { Handler } from './types'; +import { Handler, HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { validateSessionToken } from '../../utils/auth-helper'; import { getAgents, createAgent, updateAgent, deleteAgent } from '../helpers/agents'; import { UserAgentsTable } from '../models'; -export const handleAgents: Handler = async ({ orm, env, request, url }) => { +export const agentsHandler: HandlerDefinition = { + baseRoute: 'agents', + endpoints: [ + { + path: '/agents/get', + methods: ['GET'], + description: 'Get all agents for a crew' + }, + { + path: '/agents/create', + methods: ['POST'], + description: 'Create a new agent' + }, + { + path: '/agents/update', + methods: ['PUT'], + description: 'Update an existing agent' + }, + { + path: '/agents/delete', + methods: ['DELETE'], + description: 'Delete an agent' + } + ], + handler: async ({ orm, env, request, url }) => { const endpoint = url.pathname.split('/').pop(); switch (endpoint) { From 5d78b41c3e6f530e3cf59a935fba07c110cb1baf Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:10:44 -0700 Subject: [PATCH 084/128] refactor: Convert tasks handler to new HandlerDefinition format --- src/database/handlers/tasks.ts | 38 ++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/database/handlers/tasks.ts b/src/database/handlers/tasks.ts index 10aba82..45471c4 100644 --- a/src/database/handlers/tasks.ts +++ b/src/database/handlers/tasks.ts @@ -1,10 +1,44 @@ -import { Handler } from './types'; +import { Handler, HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { validateSessionToken } from '../../utils/auth-helper'; import { getTask, getTasks, createTask, updateTask, deleteTask, deleteTasks } from '../helpers/tasks'; import { UserTasksTable } from '../models'; -export const handleTasks: Handler = async ({ orm, env, request, url }) => { +export const tasksHandler: HandlerDefinition = { + baseRoute: 'tasks', + endpoints: [ + { + path: '/tasks/get', + methods: ['GET'], + description: 'Get a specific task by ID' + }, + { + path: '/tasks/list', + methods: ['GET'], + description: 'Get all tasks for an agent' + }, + { + path: '/tasks/create', + methods: ['POST'], + description: 'Create a new task' + }, + { + path: '/tasks/update', + methods: ['PUT'], + description: 'Update an existing task' + }, + { + path: '/tasks/delete', + methods: ['DELETE'], + description: 'Delete a specific task' + }, + { + path: '/tasks/delete-all', + methods: ['DELETE'], + description: 'Delete all tasks for an agent' + } + ], + handler: async ({ orm, env, request, url }) => { const endpoint = url.pathname.split('/').pop(); switch (endpoint) { From f9aadfdd5fc88d19b63b636d39f081c666d684df Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:11:24 -0700 Subject: [PATCH 085/128] refactor: Convert crews handler to new HandlerDefinition format --- src/database/handlers/crews.ts | 63 ++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/src/database/handlers/crews.ts b/src/database/handlers/crews.ts index a0fb93b..d08f59d 100644 --- a/src/database/handlers/crews.ts +++ b/src/database/handlers/crews.ts @@ -1,4 +1,4 @@ -import { Handler } from './types'; +import { Handler, HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { validateSessionToken } from '../../utils/auth-helper'; import { @@ -16,7 +16,66 @@ import { } from '../helpers/crews'; import { UserCrewsTable, UserCrewExecutionStepsTable } from '../models'; -export const handleCrews: Handler = async ({ orm, env, request, url }) => { +export const crewsHandler: HandlerDefinition = { + baseRoute: 'crews', + endpoints: [ + { + path: '/crews/profile', + methods: ['GET'], + description: 'Get crews for a specific profile' + }, + { + path: '/crews/public', + methods: ['GET'], + description: 'Get all public crews' + }, + { + path: '/crews/get', + methods: ['GET'], + description: 'Get a specific crew by ID' + }, + { + path: '/crews/create', + methods: ['POST'], + description: 'Create a new crew' + }, + { + path: '/crews/update', + methods: ['PUT'], + description: 'Update an existing crew' + }, + { + path: '/crews/delete', + methods: ['DELETE'], + description: 'Delete a crew' + }, + { + path: '/crews/executions', + methods: ['GET'], + description: 'Get crew executions for an address' + }, + { + path: '/crews/executions/add', + methods: ['POST'], + description: 'Add a new crew execution' + }, + { + path: '/crews/steps/get', + methods: ['GET'], + description: 'Get execution steps' + }, + { + path: '/crews/steps/create', + methods: ['POST'], + description: 'Create a new execution step' + }, + { + path: '/crews/steps/delete', + methods: ['DELETE'], + description: 'Delete execution steps' + } + ], + handler: async ({ orm, env, request, url }) => { const endpoint = url.pathname.split('/').pop(); switch (endpoint) { From 5aa16b2c78fc8e45dac148b588d84b7393c92fa7 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:11:51 -0700 Subject: [PATCH 086/128] refactor: Convert crons handler to new handler definition format --- src/database/handlers/crons.ts | 38 ++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/database/handlers/crons.ts b/src/database/handlers/crons.ts index a65d5e1..0de5cc1 100644 --- a/src/database/handlers/crons.ts +++ b/src/database/handlers/crons.ts @@ -1,10 +1,44 @@ -import { Handler } from './types'; +import { Handler, HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { validateSessionToken } from '../../utils/auth-helper'; import { getCronsByCrew, createCron, updateCronInput, toggleCronStatus, getEnabledCrons, getEnabledCronsDetailed } from '../helpers/crons'; import { UserCronsTable } from '../models'; -export const handleCrons: Handler = async ({ orm, env, request, url }) => { +export const cronsHandler: HandlerDefinition = { + baseRoute: 'crons', + endpoints: [ + { + path: '/crons/enabled', + methods: ['GET'], + description: 'Get all enabled crons' + }, + { + path: '/crons/enabled-detailed', + methods: ['GET'], + description: 'Get detailed information about enabled crons' + }, + { + path: '/crons/get', + methods: ['GET'], + description: 'Get crons for a specific crew' + }, + { + path: '/crons/create', + methods: ['POST'], + description: 'Create a new cron' + }, + { + path: '/crons/update', + methods: ['PUT'], + description: 'Update cron input' + }, + { + path: '/crons/toggle', + methods: ['PUT'], + description: 'Toggle cron status' + } + ], + handler: async ({ orm, env, request, url }) => { const endpoint = url.pathname.split('/').pop(); switch (endpoint) { From 4b499cd3423f9988d165a5ad13622f1f10fe2426 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:12:10 -0700 Subject: [PATCH 087/128] refactor: Convert twitter handler to new handler definition format --- src/database/handlers/twitter.ts | 48 ++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/database/handlers/twitter.ts b/src/database/handlers/twitter.ts index ba3be05..005d6f7 100644 --- a/src/database/handlers/twitter.ts +++ b/src/database/handlers/twitter.ts @@ -1,9 +1,53 @@ -import { Handler } from './types'; +import { Handler, HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { getAuthor, addAuthor, getTweet, getThreadTweets, getAuthorTweets, addTweet, getTweetLogs, addLog } from '../helpers/twitter'; import { XBotAuthorsTable, XBotTweetsTable, XBotLogsTable } from '../models'; -export const handleTwitter: Handler = async ({ orm, request, url }) => { +export const twitterHandler: HandlerDefinition = { + baseRoute: 'twitter', + endpoints: [ + { + path: '/twitter/get', + methods: ['GET'], + description: 'Get author by ID' + }, + { + path: '/twitter/create', + methods: ['POST'], + description: 'Create a new author' + }, + { + path: '/twitter/tweet', + methods: ['GET'], + description: 'Get tweet by ID' + }, + { + path: '/twitter/thread', + methods: ['GET'], + description: 'Get tweets in a thread' + }, + { + path: '/twitter/author-tweets', + methods: ['GET'], + description: 'Get tweets by author' + }, + { + path: '/twitter/add-tweet', + methods: ['POST'], + description: 'Add a new tweet' + }, + { + path: '/twitter/logs', + methods: ['GET'], + description: 'Get logs for a tweet' + }, + { + path: '/twitter/add-log', + methods: ['POST'], + description: 'Add a new log entry' + } + ], + handler: async ({ orm, request, url }) => { const endpoint = url.pathname.split('/').pop(); switch (endpoint) { From eb1fe42371fe2c61e4278fd3306b2daad6a8145c Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Mon, 23 Dec 2024 13:16:36 -0700 Subject: [PATCH 088/128] human intervention: cleanup trailing braces, remove unused type --- src/database/handlers/agents.ts | 182 +++++---- src/database/handlers/conversations.ts | 147 +++---- src/database/handlers/crews.ts | 545 +++++++++++++------------ src/database/handlers/crons.ts | 229 +++++------ src/database/handlers/index.ts | 38 +- src/database/handlers/profiles.ts | 253 ++++++------ src/database/handlers/tasks.ts | 251 ++++++------ src/database/handlers/twitter.ts | 295 ++++++------- src/database/handlers/types.ts | 20 +- 9 files changed, 984 insertions(+), 976 deletions(-) diff --git a/src/database/handlers/agents.ts b/src/database/handlers/agents.ts index 45da72f..83d6160 100644 --- a/src/database/handlers/agents.ts +++ b/src/database/handlers/agents.ts @@ -1,105 +1,109 @@ -import { Handler, HandlerDefinition } from './types'; +import { HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { validateSessionToken } from '../../utils/auth-helper'; import { getAgents, createAgent, updateAgent, deleteAgent } from '../helpers/agents'; import { UserAgentsTable } from '../models'; export const agentsHandler: HandlerDefinition = { - baseRoute: 'agents', - endpoints: [ - { - path: '/agents/get', - methods: ['GET'], - description: 'Get all agents for a crew' - }, - { - path: '/agents/create', - methods: ['POST'], - description: 'Create a new agent' - }, - { - path: '/agents/update', - methods: ['PUT'], - description: 'Update an existing agent' - }, - { - path: '/agents/delete', - methods: ['DELETE'], - description: 'Delete an agent' - } - ], - handler: async ({ orm, env, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + baseRoute: 'agents', + endpoints: [ + { + path: '/agents/get', + methods: ['GET'], + description: 'Get all agents for a crew', + }, + { + path: '/agents/create', + methods: ['POST'], + description: 'Create a new agent', + }, + { + path: '/agents/update', + methods: ['PUT'], + description: 'Update an existing agent', + }, + { + path: '/agents/delete', + methods: ['DELETE'], + description: 'Delete an agent', + }, + ], + handler: async ({ orm, env, request, url }) => { + const endpoint = url.pathname.split('/').pop(); - switch (endpoint) { - case 'get': { - const crewId = url.searchParams.get('crewId'); - if (!crewId) { - return createApiResponse('Missing crewId parameter', 400); + switch (endpoint) { + case 'get': { + const crewId = url.searchParams.get('crewId'); + if (!crewId) { + return createApiResponse('Missing crewId parameter', 400); + } + const agents = await getAgents(orm, parseInt(crewId)); + return createApiResponse({ + message: 'Successfully retrieved agents', + data: { agents }, + }); } - const agents = await getAgents(orm, parseInt(crewId)); - return createApiResponse({ - message: 'Successfully retrieved agents', - data: { agents }, - }); - } - case 'create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const agentData = (await request.json()) as Omit; - if ( - !agentData.profile_id || - !agentData.crew_id || - !agentData.agent_name || - !agentData.agent_role || - !agentData.agent_goal || - !agentData.agent_backstory - ) { - return createApiResponse('Missing required fields: profile_id, crew_id, agent_name, agent_role, agent_goal, agent_backstory', 400); + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const agentData = (await request.json()) as Omit; + if ( + !agentData.profile_id || + !agentData.crew_id || + !agentData.agent_name || + !agentData.agent_role || + !agentData.agent_goal || + !agentData.agent_backstory + ) { + return createApiResponse( + 'Missing required fields: profile_id, crew_id, agent_name, agent_role, agent_goal, agent_backstory', + 400 + ); + } + const agent = await createAgent(orm, agentData); + return createApiResponse({ + message: 'Successfully created agent', + data: { agent }, + }); } - const agent = await createAgent(orm, agentData); - return createApiResponse({ - message: 'Successfully created agent', - data: { agent }, - }); - } - case 'update': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const agentId = url.searchParams.get('id'); - if (!agentId) { - return createApiResponse('Missing id parameter', 400); + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const agentId = url.searchParams.get('id'); + if (!agentId) { + return createApiResponse('Missing id parameter', 400); + } + const updates = (await request.json()) as Partial< + Omit + >; + const result = await updateAgent(orm, parseInt(agentId), updates); + return createApiResponse({ + message: 'Successfully updated agent', + data: { result }, + }); } - const updates = (await request.json()) as Partial< - Omit - >; - const result = await updateAgent(orm, parseInt(agentId), updates); - return createApiResponse({ - message: 'Successfully updated agent', - data: { result }, - }); - } - case 'delete': { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const agentId = url.searchParams.get('id'); - if (!agentId) { - return createApiResponse('Missing id parameter', 400); + case 'delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const agentId = url.searchParams.get('id'); + if (!agentId) { + return createApiResponse('Missing id parameter', 400); + } + const result = await deleteAgent(orm, parseInt(agentId)); + return createApiResponse({ + message: 'Successfully deleted agent', + data: { result }, + }); } - const result = await deleteAgent(orm, parseInt(agentId)); - return createApiResponse({ - message: 'Successfully deleted agent', - data: { result }, - }); - } - default: - return createApiResponse(`Unsupported agents endpoint: ${endpoint}`, 404); - } + default: + return createApiResponse(`Unsupported agents endpoint: ${endpoint}`, 404); + } + }, }; diff --git a/src/database/handlers/conversations.ts b/src/database/handlers/conversations.ts index 59c0b42..6514eb6 100644 --- a/src/database/handlers/conversations.ts +++ b/src/database/handlers/conversations.ts @@ -1,84 +1,85 @@ -import { Handler, HandlerDefinition } from './types'; +import { HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { getConversations, getLatestConversation, getConversationHistory, addConversation } from '../helpers/conversations'; import { UserConversationsTable } from '../models'; export const conversationsHandler: HandlerDefinition = { - baseRoute: 'conversations', - endpoints: [ - { - path: '/conversations/conversations', - methods: ['GET'], - description: 'Get all conversations for an address' - }, - { - path: '/conversations/latest', - methods: ['GET'], - description: 'Get latest conversation for an address' - }, - { - path: '/conversations/history', - methods: ['GET'], - description: 'Get conversation history by ID' - }, - { - path: '/conversations/create', - methods: ['POST'], - description: 'Create a new conversation' - } - ], - handler: async ({ orm, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + baseRoute: 'conversations', + endpoints: [ + { + path: '/conversations/conversations', + methods: ['GET'], + description: 'Get all conversations for an address', + }, + { + path: '/conversations/latest', + methods: ['GET'], + description: 'Get latest conversation for an address', + }, + { + path: '/conversations/history', + methods: ['GET'], + description: 'Get conversation history by ID', + }, + { + path: '/conversations/create', + methods: ['POST'], + description: 'Create a new conversation', + }, + ], + handler: async ({ orm, request, url }) => { + const endpoint = url.pathname.split('/').pop(); - switch (endpoint) { - case 'conversations': - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const conversations = await getConversations(orm, address); - return createApiResponse({ - message: 'Successfully retrieved conversations', - data: conversations, - }); + switch (endpoint) { + case 'conversations': + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const conversations = await getConversations(orm, address); + return createApiResponse({ + message: 'Successfully retrieved conversations', + data: conversations, + }); - case 'latest': - const latestAddress = url.searchParams.get('address'); - if (!latestAddress) { - return createApiResponse('Missing address parameter', 400); - } - const conversation = await getLatestConversation(orm, latestAddress); - return createApiResponse({ - message: 'Successfully retrieved latest conversation', - data: conversation, - }); + case 'latest': + const latestAddress = url.searchParams.get('address'); + if (!latestAddress) { + return createApiResponse('Missing address parameter', 400); + } + const conversation = await getLatestConversation(orm, latestAddress); + return createApiResponse({ + message: 'Successfully retrieved latest conversation', + data: conversation, + }); - case 'history': - const conversationId = url.searchParams.get('id'); - if (!conversationId) { - return createApiResponse('Missing conversation ID parameter', 400); - } - const history = await getConversationHistory(orm, parseInt(conversationId)); - return createApiResponse({ - message: 'Successfully retrieved conversation history', - data: history, - }); + case 'history': + const conversationId = url.searchParams.get('id'); + if (!conversationId) { + return createApiResponse('Missing conversation ID parameter', 400); + } + const history = await getConversationHistory(orm, parseInt(conversationId)); + return createApiResponse({ + message: 'Successfully retrieved conversation history', + data: history, + }); - case 'create': - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const { profile_id, conversation_name } = (await request.json()) as UserConversationsTable; - if (!profile_id) { - return createApiResponse('Missing required field: address', 400); - } - const result = await addConversation(orm, profile_id, conversation_name ? conversation_name : 'new conversation'); - return createApiResponse({ - message: 'Successfully created conversation', - data: { result }, - }); + case 'create': + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const { profile_id, conversation_name } = (await request.json()) as UserConversationsTable; + if (!profile_id) { + return createApiResponse('Missing required field: address', 400); + } + const result = await addConversation(orm, profile_id, conversation_name ? conversation_name : 'new conversation'); + return createApiResponse({ + message: 'Successfully created conversation', + data: { result }, + }); - default: - return createApiResponse(`Unsupported conversations endpoint: ${endpoint}`, 404); - } + default: + return createApiResponse(`Unsupported conversations endpoint: ${endpoint}`, 404); + } + }, }; diff --git a/src/database/handlers/crews.ts b/src/database/handlers/crews.ts index d08f59d..be35a55 100644 --- a/src/database/handlers/crews.ts +++ b/src/database/handlers/crews.ts @@ -1,4 +1,4 @@ -import { Handler, HandlerDefinition } from './types'; +import { HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { validateSessionToken } from '../../utils/auth-helper'; import { @@ -17,298 +17,299 @@ import { import { UserCrewsTable, UserCrewExecutionStepsTable } from '../models'; export const crewsHandler: HandlerDefinition = { - baseRoute: 'crews', - endpoints: [ - { - path: '/crews/profile', - methods: ['GET'], - description: 'Get crews for a specific profile' - }, - { - path: '/crews/public', - methods: ['GET'], - description: 'Get all public crews' - }, - { - path: '/crews/get', - methods: ['GET'], - description: 'Get a specific crew by ID' - }, - { - path: '/crews/create', - methods: ['POST'], - description: 'Create a new crew' - }, - { - path: '/crews/update', - methods: ['PUT'], - description: 'Update an existing crew' - }, - { - path: '/crews/delete', - methods: ['DELETE'], - description: 'Delete a crew' - }, - { - path: '/crews/executions', - methods: ['GET'], - description: 'Get crew executions for an address' - }, - { - path: '/crews/executions/add', - methods: ['POST'], - description: 'Add a new crew execution' - }, - { - path: '/crews/steps/get', - methods: ['GET'], - description: 'Get execution steps' - }, - { - path: '/crews/steps/create', - methods: ['POST'], - description: 'Create a new execution step' - }, - { - path: '/crews/steps/delete', - methods: ['DELETE'], - description: 'Delete execution steps' - } - ], - handler: async ({ orm, env, request, url }) => { - const endpoint = url.pathname.split('/').pop(); - - switch (endpoint) { - case 'profile': { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); + baseRoute: 'crews', + endpoints: [ + { + path: '/crews/profile', + methods: ['GET'], + description: 'Get crews for a specific profile', + }, + { + path: '/crews/public', + methods: ['GET'], + description: 'Get all public crews', + }, + { + path: '/crews/get', + methods: ['GET'], + description: 'Get a specific crew by ID', + }, + { + path: '/crews/create', + methods: ['POST'], + description: 'Create a new crew', + }, + { + path: '/crews/update', + methods: ['PUT'], + description: 'Update an existing crew', + }, + { + path: '/crews/delete', + methods: ['DELETE'], + description: 'Delete a crew', + }, + { + path: '/crews/executions', + methods: ['GET'], + description: 'Get crew executions for an address', + }, + { + path: '/crews/executions/add', + methods: ['POST'], + description: 'Add a new crew execution', + }, + { + path: '/crews/steps/get', + methods: ['GET'], + description: 'Get execution steps', + }, + { + path: '/crews/steps/create', + methods: ['POST'], + description: 'Create a new execution step', + }, + { + path: '/crews/steps/delete', + methods: ['DELETE'], + description: 'Delete execution steps', + }, + ], + handler: async ({ orm, env, request, url }) => { + const endpoint = url.pathname.split('/').pop(); + + switch (endpoint) { + case 'profile': { + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + + // Get the session token from Authorization header + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + + // Extract token from Bearer format + const token = authHeader.replace('Bearer ', ''); + + // Verify the token matches the requested address + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success || tokenAddress.address !== address) { + return createApiResponse('Unauthorized access', 403); + } + + const crews = await getCrewsByProfile(orm, address); + return createApiResponse({ + message: 'Successfully retrieved profile crews', + data: { crews }, + }); } - // Get the session token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader) { - return createApiResponse('Missing authorization header', 401); + case 'public': { + const crews = await getPublicCrews(orm); + return createApiResponse({ + message: 'Successfully retrieved public crews', + data: { crews }, + }); } - // Extract token from Bearer format - const token = authHeader.replace('Bearer ', ''); - - // Verify the token matches the requested address - const tokenAddress = await validateSessionToken(env, token); - if (!tokenAddress.success || tokenAddress.address !== address) { - return createApiResponse('Unauthorized access', 403); - } - - const crews = await getCrewsByProfile(orm, address); - return createApiResponse({ - message: 'Successfully retrieved profile crews', - data: { crews }, - }); - } - - case 'public': { - const crews = await getPublicCrews(orm); - return createApiResponse({ - message: 'Successfully retrieved public crews', - data: { crews }, - }); - } - - case 'get': { - const crewId = url.searchParams.get('id'); - if (!crewId) { - return createApiResponse('Missing id parameter', 400); - } - const crew = await getCrew(orm, parseInt(crewId)); - return createApiResponse({ - message: 'Successfully retrieved crew', - data: { crew }, - }); - } - - case 'create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const crewData = (await request.json()) as Omit; - if (!crewData.profile_id || !crewData.crew_name) { - return createApiResponse('Missing required fields: profile_id, crew_name', 400); + case 'get': { + const crewId = url.searchParams.get('id'); + if (!crewId) { + return createApiResponse('Missing id parameter', 400); + } + const crew = await getCrew(orm, parseInt(crewId)); + return createApiResponse({ + message: 'Successfully retrieved crew', + data: { crew }, + }); } - const crew = await createCrew(orm, crewData); - return createApiResponse({ - message: 'Successfully created crew', - data: { crew }, - }); - } - case 'update': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const crewData = (await request.json()) as Omit; + if (!crewData.profile_id || !crewData.crew_name) { + return createApiResponse('Missing required fields: profile_id, crew_name', 400); + } + const crew = await createCrew(orm, crewData); + return createApiResponse({ + message: 'Successfully created crew', + data: { crew }, + }); } - const crewId = url.searchParams.get('id'); - if (!crewId) { - return createApiResponse('Missing id parameter', 400); - } - const updates = (await request.json()) as Partial>; - const result = await updateCrew(orm, parseInt(crewId), updates); - return createApiResponse({ - message: 'Successfully updated crew', - data: { result }, - }); - } - case 'delete': { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const crewId = url.searchParams.get('id'); - if (!crewId) { - return createApiResponse('Missing id parameter', 400); + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const crewId = url.searchParams.get('id'); + if (!crewId) { + return createApiResponse('Missing id parameter', 400); + } + const updates = (await request.json()) as Partial>; + const result = await updateCrew(orm, parseInt(crewId), updates); + return createApiResponse({ + message: 'Successfully updated crew', + data: { result }, + }); } - const result = await deleteCrew(orm, parseInt(crewId)); - return createApiResponse({ - message: 'Successfully deleted crew', - data: { result }, - }); - } - case 'executions': { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); + case 'delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const crewId = url.searchParams.get('id'); + if (!crewId) { + return createApiResponse('Missing id parameter', 400); + } + const result = await deleteCrew(orm, parseInt(crewId)); + return createApiResponse({ + message: 'Successfully deleted crew', + data: { result }, + }); } - const executions = await getCrewExecutions(orm, address); - return createApiResponse({ - message: 'Successfully retrieved crew executions', - data: { executions }, - }); - } - case 'executions/add': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); + case 'executions': { + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const executions = await getCrewExecutions(orm, address); + return createApiResponse({ + message: 'Successfully retrieved crew executions', + data: { executions }, + }); } - type AddCrewExecutionRequest = { - address: string; - crewId: number; - conversationId: number; - input: string; - }; - - const body: AddCrewExecutionRequest = await request.json(); - const { address, crewId, conversationId, input } = body; - - if (!address || !crewId || !conversationId || !input) { - return createApiResponse('Missing required parameters: address, crewId, conversationId, input', 400); + case 'executions/add': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + + type AddCrewExecutionRequest = { + address: string; + crewId: number; + conversationId: number; + input: string; + }; + + const body: AddCrewExecutionRequest = await request.json(); + const { address, crewId, conversationId, input } = body; + + if (!address || !crewId || !conversationId || !input) { + return createApiResponse('Missing required parameters: address, crewId, conversationId, input', 400); + } + + const execution = await addCrewExecution(orm, address, crewId, conversationId, input); + return createApiResponse({ + message: 'Successfully created crew execution', + data: { execution }, + }); } - const execution = await addCrewExecution(orm, address, crewId, conversationId, input); - return createApiResponse({ - message: 'Successfully created crew execution', - data: { execution }, - }); - } - - case 'steps/get': { - const executionId = url.searchParams.get('executionId'); - if (!executionId) { - return createApiResponse('Missing executionId parameter', 400); + case 'steps/get': { + const executionId = url.searchParams.get('executionId'); + if (!executionId) { + return createApiResponse('Missing executionId parameter', 400); + } + + // Get the session token from Authorization header + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + + // Extract token from Bearer format + const token = authHeader.replace('Bearer ', ''); + + // Verify the token matches the requested address + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success) { + return createApiResponse('Unauthorized access', 403); + } + + const steps = await getExecutionSteps(orm, parseInt(executionId)); + return createApiResponse({ + message: 'Successfully retrieved execution steps', + data: { steps }, + }); } - // Get the session token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader) { - return createApiResponse('Missing authorization header', 401); + case 'steps/create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + + // Get the session token from Authorization header + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + + // Extract token from Bearer format + const token = authHeader.replace('Bearer ', ''); + + // Verify the token + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success) { + return createApiResponse('Unauthorized access', 403); + } + + const stepData = (await request.json()) as UserCrewExecutionStepsTable; + if (!stepData.profile_id || !stepData.crew_id || !stepData.execution_id || !stepData.step_type || !stepData.step_data) { + return createApiResponse('Missing required fields: profile_id, crew_id, execution_id, step_type, step_data', 400); + } + + // Verify the profile_id matches the token address + if (stepData.profile_id !== tokenAddress.address) { + return createApiResponse('Unauthorized: profile_id does not match token', 403); + } + + const step = await createExecutionStep(orm, stepData); + return createApiResponse({ + message: 'Successfully created execution step', + data: { step }, + }); } - // Extract token from Bearer format - const token = authHeader.replace('Bearer ', ''); - - // Verify the token matches the requested address - const tokenAddress = await validateSessionToken(env, token); - if (!tokenAddress.success) { - return createApiResponse('Unauthorized access', 403); + case 'steps/delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + + const executionId = url.searchParams.get('executionId'); + if (!executionId) { + return createApiResponse('Missing executionId parameter', 400); + } + + // Get the session token from Authorization header + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + + // Extract token from Bearer format + const token = authHeader.replace('Bearer ', ''); + + // Verify the token + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success) { + return createApiResponse('Unauthorized access', 403); + } + + const result = await deleteExecutionSteps(orm, parseInt(executionId)); + return createApiResponse({ + message: 'Successfully deleted execution steps', + data: { result }, + }); } - const steps = await getExecutionSteps(orm, parseInt(executionId)); - return createApiResponse({ - message: 'Successfully retrieved execution steps', - data: { steps }, - }); + default: + return createApiResponse(`Unsupported crews endpoint: ${endpoint}`, 404); } - - case 'steps/create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - - // Get the session token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader) { - return createApiResponse('Missing authorization header', 401); - } - - // Extract token from Bearer format - const token = authHeader.replace('Bearer ', ''); - - // Verify the token - const tokenAddress = await validateSessionToken(env, token); - if (!tokenAddress.success) { - return createApiResponse('Unauthorized access', 403); - } - - const stepData = (await request.json()) as UserCrewExecutionStepsTable; - if (!stepData.profile_id || !stepData.crew_id || !stepData.execution_id || !stepData.step_type || !stepData.step_data) { - return createApiResponse('Missing required fields: profile_id, crew_id, execution_id, step_type, step_data', 400); - } - - // Verify the profile_id matches the token address - if (stepData.profile_id !== tokenAddress.address) { - return createApiResponse('Unauthorized: profile_id does not match token', 403); - } - - const step = await createExecutionStep(orm, stepData); - return createApiResponse({ - message: 'Successfully created execution step', - data: { step }, - }); - } - - case 'steps/delete': { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - - const executionId = url.searchParams.get('executionId'); - if (!executionId) { - return createApiResponse('Missing executionId parameter', 400); - } - - // Get the session token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader) { - return createApiResponse('Missing authorization header', 401); - } - - // Extract token from Bearer format - const token = authHeader.replace('Bearer ', ''); - - // Verify the token - const tokenAddress = await validateSessionToken(env, token); - if (!tokenAddress.success) { - return createApiResponse('Unauthorized access', 403); - } - - const result = await deleteExecutionSteps(orm, parseInt(executionId)); - return createApiResponse({ - message: 'Successfully deleted execution steps', - data: { result }, - }); - } - - default: - return createApiResponse(`Unsupported crews endpoint: ${endpoint}`, 404); - } + }, }; diff --git a/src/database/handlers/crons.ts b/src/database/handlers/crons.ts index 0de5cc1..52ea399 100644 --- a/src/database/handlers/crons.ts +++ b/src/database/handlers/crons.ts @@ -1,132 +1,133 @@ -import { Handler, HandlerDefinition } from './types'; +import { HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { validateSessionToken } from '../../utils/auth-helper'; import { getCronsByCrew, createCron, updateCronInput, toggleCronStatus, getEnabledCrons, getEnabledCronsDetailed } from '../helpers/crons'; import { UserCronsTable } from '../models'; export const cronsHandler: HandlerDefinition = { - baseRoute: 'crons', - endpoints: [ - { - path: '/crons/enabled', - methods: ['GET'], - description: 'Get all enabled crons' - }, - { - path: '/crons/enabled-detailed', - methods: ['GET'], - description: 'Get detailed information about enabled crons' - }, - { - path: '/crons/get', - methods: ['GET'], - description: 'Get crons for a specific crew' - }, - { - path: '/crons/create', - methods: ['POST'], - description: 'Create a new cron' - }, - { - path: '/crons/update', - methods: ['PUT'], - description: 'Update cron input' - }, - { - path: '/crons/toggle', - methods: ['PUT'], - description: 'Toggle cron status' - } - ], - handler: async ({ orm, env, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + baseRoute: 'crons', + endpoints: [ + { + path: '/crons/enabled', + methods: ['GET'], + description: 'Get all enabled crons', + }, + { + path: '/crons/enabled-detailed', + methods: ['GET'], + description: 'Get detailed information about enabled crons', + }, + { + path: '/crons/get', + methods: ['GET'], + description: 'Get crons for a specific crew', + }, + { + path: '/crons/create', + methods: ['POST'], + description: 'Create a new cron', + }, + { + path: '/crons/update', + methods: ['PUT'], + description: 'Update cron input', + }, + { + path: '/crons/toggle', + methods: ['PUT'], + description: 'Toggle cron status', + }, + ], + handler: async ({ orm, env, request, url }) => { + const endpoint = url.pathname.split('/').pop(); - switch (endpoint) { - case 'enabled': { - const crons = await getEnabledCrons(orm); - return createApiResponse({ - message: 'Successfully retrieved enabled crons', - data: { crons }, - }); - } - - case 'enabled-detailed': { - const crons = await getEnabledCronsDetailed(orm); - return createApiResponse({ - message: 'Successfully retrieved detailed cron information', - data: { crons }, - }); - } - - case 'get': { - const crewId = url.searchParams.get('crewId'); - if (!crewId) { - return createApiResponse('Missing crewId parameter', 400); + switch (endpoint) { + case 'enabled': { + const crons = await getEnabledCrons(orm); + return createApiResponse({ + message: 'Successfully retrieved enabled crons', + data: { crons }, + }); } - const crons = await getCronsByCrew(orm, parseInt(crewId)); - return createApiResponse({ - message: 'Successfully retrieved crons for crew', - data: { crons }, - }); - } - case 'create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const cronData = (await request.json()) as UserCronsTable; - if (!cronData.profile_id || !cronData.crew_id || cronData.cron_enabled === undefined) { - return createApiResponse('Missing required fields: profile_id, crew_id, cron_enabled', 400); + case 'enabled-detailed': { + const crons = await getEnabledCronsDetailed(orm); + return createApiResponse({ + message: 'Successfully retrieved detailed cron information', + data: { crons }, + }); } - // Set defaults if not provided - cronData.cron_interval = cronData.cron_interval || '0 * * * *'; // Default to hourly - cronData.cron_input = cronData.cron_input || ''; - const cron = await createCron(orm, cronData); - return createApiResponse({ - message: 'Successfully created cron', - data: { cron }, - }); - } - case 'update': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); + case 'get': { + const crewId = url.searchParams.get('crewId'); + if (!crewId) { + return createApiResponse('Missing crewId parameter', 400); + } + const crons = await getCronsByCrew(orm, parseInt(crewId)); + return createApiResponse({ + message: 'Successfully retrieved crons for crew', + data: { crons }, + }); } - const cronId = url.searchParams.get('id'); - if (!cronId) { - return createApiResponse('Missing id parameter', 400); - } - const { cron_input } = (await request.json()) as UserCronsTable; - if (cron_input === undefined) { - return createApiResponse('Missing cron_input in request body', 400); - } - const result = await updateCronInput(orm, parseInt(cronId), cron_input); - return createApiResponse({ - message: 'Successfully updated cron input', - data: { result }, - }); - } - case 'toggle': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const cronData = (await request.json()) as UserCronsTable; + if (!cronData.profile_id || !cronData.crew_id || cronData.cron_enabled === undefined) { + return createApiResponse('Missing required fields: profile_id, crew_id, cron_enabled', 400); + } + // Set defaults if not provided + cronData.cron_interval = cronData.cron_interval || '0 * * * *'; // Default to hourly + cronData.cron_input = cronData.cron_input || ''; + const cron = await createCron(orm, cronData); + return createApiResponse({ + message: 'Successfully created cron', + data: { cron }, + }); } - const cronId = url.searchParams.get('id'); - if (!cronId) { - return createApiResponse('Missing id parameter', 400); + + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const cronId = url.searchParams.get('id'); + if (!cronId) { + return createApiResponse('Missing id parameter', 400); + } + const { cron_input } = (await request.json()) as UserCronsTable; + if (cron_input === undefined) { + return createApiResponse('Missing cron_input in request body', 400); + } + const result = await updateCronInput(orm, parseInt(cronId), cron_input); + return createApiResponse({ + message: 'Successfully updated cron input', + data: { result }, + }); } - const { cron_enabled } = (await request.json()) as UserCronsTable; - if (cron_enabled === undefined) { - return createApiResponse('Missing cron_enabled in request body', 400); + + case 'toggle': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const cronId = url.searchParams.get('id'); + if (!cronId) { + return createApiResponse('Missing id parameter', 400); + } + const { cron_enabled } = (await request.json()) as UserCronsTable; + if (cron_enabled === undefined) { + return createApiResponse('Missing cron_enabled in request body', 400); + } + const result = await toggleCronStatus(orm, parseInt(cronId), cron_enabled ? 1 : 0); + return createApiResponse({ + message: 'Successfully toggled cron status', + data: { result }, + }); } - const result = await toggleCronStatus(orm, parseInt(cronId), cron_enabled ? 1 : 0); - return createApiResponse({ - message: 'Successfully toggled cron status', - data: { result }, - }); - } - default: - return createApiResponse(`Unsupported crons endpoint: ${endpoint}`, 404); - } + default: + return createApiResponse(`Unsupported crons endpoint: ${endpoint}`, 404); + } + }, }; diff --git a/src/database/handlers/index.ts b/src/database/handlers/index.ts index 23898e8..a020d1a 100644 --- a/src/database/handlers/index.ts +++ b/src/database/handlers/index.ts @@ -1,37 +1,35 @@ import { Handler, HandlerDefinition } from './types'; -import { profilesHandler } from './profiles'; +import { agentsHandler } from './agents'; import { conversationsHandler } from './conversations'; import { crewsHandler } from './crews'; -import { agentsHandler } from './agents'; -import { tasksHandler } from './tasks'; import { cronsHandler } from './crons'; +import { profilesHandler } from './profiles'; +import { tasksHandler } from './tasks'; import { twitterHandler } from './twitter'; const handlerDefinitions: HandlerDefinition[] = [ - profilesHandler, - conversationsHandler, - crewsHandler, - agentsHandler, - tasksHandler, - cronsHandler, - twitterHandler + profilesHandler, + conversationsHandler, + crewsHandler, + agentsHandler, + tasksHandler, + cronsHandler, + twitterHandler, ]; export const getHandler = (path: string): Handler | undefined => { - const segment = path.split('/')[1]; - const definition = handlerDefinitions.find(def => def.baseRoute === segment); - return definition?.handler; + const segment = path.split('/')[1]; + const definition = handlerDefinitions.find((def) => def.baseRoute === segment); + return definition?.handler; }; export const getSupportedEndpoints = (): string[] => { - return handlerDefinitions.flatMap(def => - def.endpoints.map(endpoint => endpoint.path) - ); + return handlerDefinitions.flatMap((def) => def.endpoints.map((endpoint) => endpoint.path)); }; export const getEndpointDocumentation = () => { - return handlerDefinitions.map(def => ({ - baseRoute: def.baseRoute, - endpoints: def.endpoints - })); + return handlerDefinitions.map((def) => ({ + baseRoute: def.baseRoute, + endpoints: def.endpoints, + })); }; diff --git a/src/database/handlers/profiles.ts b/src/database/handlers/profiles.ts index 3ffd3c0..9d58227 100644 --- a/src/database/handlers/profiles.ts +++ b/src/database/handlers/profiles.ts @@ -1,4 +1,4 @@ -import { Handler, HandlerDefinition } from './types'; +import { HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { getUserRole, @@ -12,143 +12,144 @@ import { import { UserProfilesTable } from '../models'; export const profilesHandler: HandlerDefinition = { - baseRoute: 'profiles', - endpoints: [ - { - path: '/profiles/role', - methods: ['GET'], - description: 'Get user role by address' - }, - { - path: '/profiles/get', - methods: ['GET'], - description: 'Get user profile by address' - }, - { - path: '/profiles/create', - methods: ['POST'], - description: 'Create new user profile' - }, - { - path: '/profiles/update', - methods: ['PUT'], - description: 'Update existing user profile' - }, - { - path: '/profiles/delete', - methods: ['DELETE'], - description: 'Delete user profile' - }, - { - path: '/profiles/list', - methods: ['GET'], - description: 'Get all user profiles' - }, - { - path: '/profiles/admin-update', - methods: ['PUT'], - description: 'Admin update of user profile by ID' - } - ], - handler: async ({ orm, env, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + baseRoute: 'profiles', + endpoints: [ + { + path: '/profiles/role', + methods: ['GET'], + description: 'Get user role by address', + }, + { + path: '/profiles/get', + methods: ['GET'], + description: 'Get user profile by address', + }, + { + path: '/profiles/create', + methods: ['POST'], + description: 'Create new user profile', + }, + { + path: '/profiles/update', + methods: ['PUT'], + description: 'Update existing user profile', + }, + { + path: '/profiles/delete', + methods: ['DELETE'], + description: 'Delete user profile', + }, + { + path: '/profiles/list', + methods: ['GET'], + description: 'Get all user profiles', + }, + { + path: '/profiles/admin-update', + methods: ['PUT'], + description: 'Admin update of user profile by ID', + }, + ], + handler: async ({ orm, env, request, url }) => { + const endpoint = url.pathname.split('/').pop(); - switch (endpoint) { - case 'role': { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); + switch (endpoint) { + case 'role': { + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const role = await getUserRole(orm, address); + return createApiResponse({ + message: 'Successfully retrieved user role', + data: { role }, + }); } - const role = await getUserRole(orm, address); - return createApiResponse({ - message: 'Successfully retrieved user role', - data: { role }, - }); - } - case 'get': { - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); + case 'get': { + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const profile = await getUserProfile(orm, address); + return createApiResponse({ + message: 'Successfully retrieved user profile', + data: { profile }, + }); } - const profile = await getUserProfile(orm, address); - return createApiResponse({ - message: 'Successfully retrieved user profile', - data: { profile }, - }); - } - case 'create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const profileData = (await request.json()) as UserProfilesTable; + if (!profileData.stx_address || !profileData.user_role) { + return createApiResponse('Missing required fields: stx_address, user_role', 400); + } + const profile = await createUserProfile(orm, profileData); + return createApiResponse({ + message: 'Successfully created user profile', + data: { profile }, + }); } - const profileData = (await request.json()) as UserProfilesTable; - if (!profileData.stx_address || !profileData.user_role) { - return createApiResponse('Missing required fields: stx_address, user_role', 400); - } - const profile = await createUserProfile(orm, profileData); - return createApiResponse({ - message: 'Successfully created user profile', - data: { profile }, - }); - } - case 'update': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const profileData = (await request.json()) as UserProfilesTable; + const result = await updateUserProfile(orm, address, profileData); + return createApiResponse({ + message: 'Successfully updated user profile', + data: { result }, + }); } - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); - } - const profileData = (await request.json()) as UserProfilesTable; - const result = await updateUserProfile(orm, address, profileData); - return createApiResponse({ - message: 'Successfully updated user profile', - data: { result }, - }); - } - case 'delete': { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const address = url.searchParams.get('address'); - if (!address) { - return createApiResponse('Missing address parameter', 400); + case 'delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const address = url.searchParams.get('address'); + if (!address) { + return createApiResponse('Missing address parameter', 400); + } + const result = await deleteUserProfile(orm, address); + return createApiResponse({ + message: 'Successfully deleted user profile', + data: { result }, + }); } - const result = await deleteUserProfile(orm, address); - return createApiResponse({ - message: 'Successfully deleted user profile', - data: { result }, - }); - } - - case 'list': { - const profiles = await getAllUserProfiles(orm); - return createApiResponse({ - message: 'Successfully retrieved all user profiles', - data: { profiles }, - }); - } - case 'admin-update': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); + case 'list': { + const profiles = await getAllUserProfiles(orm); + return createApiResponse({ + message: 'Successfully retrieved all user profiles', + data: { profiles }, + }); } - const userId = url.searchParams.get('userId'); - if (!userId) { - return createApiResponse('Missing userId parameter', 400); + + case 'admin-update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const userId = url.searchParams.get('userId'); + if (!userId) { + return createApiResponse('Missing userId parameter', 400); + } + const updates = (await request.json()) as UserProfilesTable; + const result = await updateUserProfileById(orm, parseInt(userId), updates); + return createApiResponse({ + message: 'Successfully updated user profile', + data: { result }, + }); } - const updates = (await request.json()) as UserProfilesTable; - const result = await updateUserProfileById(orm, parseInt(userId), updates); - return createApiResponse({ - message: 'Successfully updated user profile', - data: { result }, - }); - } - default: - return createApiResponse(`Unsupported profiles endpoint: ${endpoint}`, 404); - } + default: + return createApiResponse(`Unsupported profiles endpoint: ${endpoint}`, 404); + } + }, }; diff --git a/src/database/handlers/tasks.ts b/src/database/handlers/tasks.ts index 45471c4..c51de86 100644 --- a/src/database/handlers/tasks.ts +++ b/src/database/handlers/tasks.ts @@ -1,145 +1,146 @@ -import { Handler, HandlerDefinition } from './types'; +import { HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { validateSessionToken } from '../../utils/auth-helper'; import { getTask, getTasks, createTask, updateTask, deleteTask, deleteTasks } from '../helpers/tasks'; import { UserTasksTable } from '../models'; export const tasksHandler: HandlerDefinition = { - baseRoute: 'tasks', - endpoints: [ - { - path: '/tasks/get', - methods: ['GET'], - description: 'Get a specific task by ID' - }, - { - path: '/tasks/list', - methods: ['GET'], - description: 'Get all tasks for an agent' - }, - { - path: '/tasks/create', - methods: ['POST'], - description: 'Create a new task' - }, - { - path: '/tasks/update', - methods: ['PUT'], - description: 'Update an existing task' - }, - { - path: '/tasks/delete', - methods: ['DELETE'], - description: 'Delete a specific task' - }, - { - path: '/tasks/delete-all', - methods: ['DELETE'], - description: 'Delete all tasks for an agent' - } - ], - handler: async ({ orm, env, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + baseRoute: 'tasks', + endpoints: [ + { + path: '/tasks/get', + methods: ['GET'], + description: 'Get a specific task by ID', + }, + { + path: '/tasks/list', + methods: ['GET'], + description: 'Get all tasks for an agent', + }, + { + path: '/tasks/create', + methods: ['POST'], + description: 'Create a new task', + }, + { + path: '/tasks/update', + methods: ['PUT'], + description: 'Update an existing task', + }, + { + path: '/tasks/delete', + methods: ['DELETE'], + description: 'Delete a specific task', + }, + { + path: '/tasks/delete-all', + methods: ['DELETE'], + description: 'Delete all tasks for an agent', + }, + ], + handler: async ({ orm, env, request, url }) => { + const endpoint = url.pathname.split('/').pop(); - switch (endpoint) { - case 'get': { - const taskId = url.searchParams.get('id'); - if (!taskId) { - return createApiResponse('Missing id parameter', 400); + switch (endpoint) { + case 'get': { + const taskId = url.searchParams.get('id'); + if (!taskId) { + return createApiResponse('Missing id parameter', 400); + } + const task = await getTask(orm, parseInt(taskId)); + return createApiResponse({ + message: 'Successfully retrieved task', + data: { task }, + }); } - const task = await getTask(orm, parseInt(taskId)); - return createApiResponse({ - message: 'Successfully retrieved task', - data: { task }, - }); - } - case 'list': { - const agentId = url.searchParams.get('agentId'); - if (!agentId) { - return createApiResponse('Missing agentId parameter', 400); + case 'list': { + const agentId = url.searchParams.get('agentId'); + if (!agentId) { + return createApiResponse('Missing agentId parameter', 400); + } + const tasks = await getTasks(orm, parseInt(agentId)); + return createApiResponse({ + message: 'Successfully retrieved tasks', + data: { tasks }, + }); } - const tasks = await getTasks(orm, parseInt(agentId)); - return createApiResponse({ - message: 'Successfully retrieved tasks', - data: { tasks }, - }); - } - case 'create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const taskData = (await request.json()) as UserTasksTable; - if ( - !taskData.profile_id || - !taskData.crew_id || - !taskData.agent_id || - !taskData.task_name || - !taskData.task_description || - !taskData.task_expected_output - ) { - return createApiResponse( - 'Missing required fields: profile_id, crew_id, agent_id, task_name, task_description, task_expected_output', - 400 - ); + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const taskData = (await request.json()) as UserTasksTable; + if ( + !taskData.profile_id || + !taskData.crew_id || + !taskData.agent_id || + !taskData.task_name || + !taskData.task_description || + !taskData.task_expected_output + ) { + return createApiResponse( + 'Missing required fields: profile_id, crew_id, agent_id, task_name, task_description, task_expected_output', + 400 + ); + } + const task = await createTask(orm, taskData); + return createApiResponse({ + message: 'Successfully created task', + data: { task }, + }); } - const task = await createTask(orm, taskData); - return createApiResponse({ - message: 'Successfully created task', - data: { task }, - }); - } - case 'update': { - if (request.method !== 'PUT') { - return createApiResponse('Method not allowed', 405); - } - const taskId = url.searchParams.get('id'); - if (!taskId) { - return createApiResponse('Missing id parameter', 400); + case 'update': { + if (request.method !== 'PUT') { + return createApiResponse('Method not allowed', 405); + } + const taskId = url.searchParams.get('id'); + if (!taskId) { + return createApiResponse('Missing id parameter', 400); + } + const updates = (await request.json()) as Partial< + Omit + >; + const result = await updateTask(orm, parseInt(taskId), updates); + return createApiResponse({ + message: 'Successfully updated task', + data: { result }, + }); } - const updates = (await request.json()) as Partial< - Omit - >; - const result = await updateTask(orm, parseInt(taskId), updates); - return createApiResponse({ - message: 'Successfully updated task', - data: { result }, - }); - } - case 'delete': { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); + case 'delete': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const taskId = url.searchParams.get('id'); + if (!taskId) { + return createApiResponse('Missing id parameter', 400); + } + const result = await deleteTask(orm, parseInt(taskId)); + return createApiResponse({ + message: 'Successfully deleted task', + data: { result }, + }); } - const taskId = url.searchParams.get('id'); - if (!taskId) { - return createApiResponse('Missing id parameter', 400); - } - const result = await deleteTask(orm, parseInt(taskId)); - return createApiResponse({ - message: 'Successfully deleted task', - data: { result }, - }); - } - case 'delete-all': { - if (request.method !== 'DELETE') { - return createApiResponse('Method not allowed', 405); - } - const agentId = url.searchParams.get('agentId'); - if (!agentId) { - return createApiResponse('Missing agentId parameter', 400); + case 'delete-all': { + if (request.method !== 'DELETE') { + return createApiResponse('Method not allowed', 405); + } + const agentId = url.searchParams.get('agentId'); + if (!agentId) { + return createApiResponse('Missing agentId parameter', 400); + } + const result = await deleteTasks(orm, parseInt(agentId)); + return createApiResponse({ + message: 'Successfully deleted all tasks for agent', + data: { result }, + }); } - const result = await deleteTasks(orm, parseInt(agentId)); - return createApiResponse({ - message: 'Successfully deleted all tasks for agent', - data: { result }, - }); - } - default: - return createApiResponse(`Unsupported tasks endpoint: ${endpoint}`, 404); - } + default: + return createApiResponse(`Unsupported tasks endpoint: ${endpoint}`, 404); + } + }, }; diff --git a/src/database/handlers/twitter.ts b/src/database/handlers/twitter.ts index 005d6f7..260b93c 100644 --- a/src/database/handlers/twitter.ts +++ b/src/database/handlers/twitter.ts @@ -1,170 +1,171 @@ -import { Handler, HandlerDefinition } from './types'; +import { HandlerDefinition } from './types'; import { createApiResponse } from '../../utils/requests-responses'; import { getAuthor, addAuthor, getTweet, getThreadTweets, getAuthorTweets, addTweet, getTweetLogs, addLog } from '../helpers/twitter'; import { XBotAuthorsTable, XBotTweetsTable, XBotLogsTable } from '../models'; export const twitterHandler: HandlerDefinition = { - baseRoute: 'twitter', - endpoints: [ - { - path: '/twitter/get', - methods: ['GET'], - description: 'Get author by ID' - }, - { - path: '/twitter/create', - methods: ['POST'], - description: 'Create a new author' - }, - { - path: '/twitter/tweet', - methods: ['GET'], - description: 'Get tweet by ID' - }, - { - path: '/twitter/thread', - methods: ['GET'], - description: 'Get tweets in a thread' - }, - { - path: '/twitter/author-tweets', - methods: ['GET'], - description: 'Get tweets by author' - }, - { - path: '/twitter/add-tweet', - methods: ['POST'], - description: 'Add a new tweet' - }, - { - path: '/twitter/logs', - methods: ['GET'], - description: 'Get logs for a tweet' - }, - { - path: '/twitter/add-log', - methods: ['POST'], - description: 'Add a new log entry' - } - ], - handler: async ({ orm, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + baseRoute: 'twitter', + endpoints: [ + { + path: '/twitter/get', + methods: ['GET'], + description: 'Get author by ID', + }, + { + path: '/twitter/create', + methods: ['POST'], + description: 'Create a new author', + }, + { + path: '/twitter/tweet', + methods: ['GET'], + description: 'Get tweet by ID', + }, + { + path: '/twitter/thread', + methods: ['GET'], + description: 'Get tweets in a thread', + }, + { + path: '/twitter/author-tweets', + methods: ['GET'], + description: 'Get tweets by author', + }, + { + path: '/twitter/add-tweet', + methods: ['POST'], + description: 'Add a new tweet', + }, + { + path: '/twitter/logs', + methods: ['GET'], + description: 'Get logs for a tweet', + }, + { + path: '/twitter/add-log', + methods: ['POST'], + description: 'Add a new log entry', + }, + ], + handler: async ({ orm, request, url }) => { + const endpoint = url.pathname.split('/').pop(); - switch (endpoint) { - case 'get': { - const authorId = url.searchParams.get('authorId'); - if (!authorId) { - return createApiResponse('Missing authorId parameter', 400); + switch (endpoint) { + case 'get': { + const authorId = url.searchParams.get('authorId'); + if (!authorId) { + return createApiResponse('Missing authorId parameter', 400); + } + const author = await getAuthor(orm, authorId); + return createApiResponse({ + message: 'Successfully retrieved author', + data: { author }, + }); } - const author = await getAuthor(orm, authorId); - return createApiResponse({ - message: 'Successfully retrieved author', - data: { author }, - }); - } - case 'create': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); - } - const { author_id, realname, username } = (await request.json()) as XBotAuthorsTable; - if (!author_id) { - return createApiResponse('Missing required fields: authorId', 400); + case 'create': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const { author_id, realname, username } = (await request.json()) as XBotAuthorsTable; + if (!author_id) { + return createApiResponse('Missing required fields: authorId', 400); + } + const author = await addAuthor(orm, author_id, realname || undefined, username || undefined); + return createApiResponse({ + message: 'Successfully created author', + data: { author }, + }); } - const author = await addAuthor(orm, author_id, realname || undefined, username || undefined); - return createApiResponse({ - message: 'Successfully created author', - data: { author }, - }); - } - case 'tweet': { - const tweetId = url.searchParams.get('tweetId'); - if (!tweetId) { - return createApiResponse('Missing tweetId parameter', 400); + case 'tweet': { + const tweetId = url.searchParams.get('tweetId'); + if (!tweetId) { + return createApiResponse('Missing tweetId parameter', 400); + } + const tweet = await getTweet(orm, tweetId); + return createApiResponse({ + message: 'Successfully retrieved tweet', + data: { tweet }, + }); } - const tweet = await getTweet(orm, tweetId); - return createApiResponse({ - message: 'Successfully retrieved tweet', - data: { tweet }, - }); - } - case 'thread': { - const threadId = url.searchParams.get('threadId'); - if (!threadId) { - return createApiResponse('Missing threadId parameter', 400); + case 'thread': { + const threadId = url.searchParams.get('threadId'); + if (!threadId) { + return createApiResponse('Missing threadId parameter', 400); + } + const tweets = await getThreadTweets(orm, parseInt(threadId)); + return createApiResponse({ + message: 'Successfully retrieved thread tweets', + data: { tweets }, + }); } - const tweets = await getThreadTweets(orm, parseInt(threadId)); - return createApiResponse({ - message: 'Successfully retrieved thread tweets', - data: { tweets }, - }); - } - case 'author-tweets': { - const authorId = url.searchParams.get('authorId'); - if (!authorId) { - return createApiResponse('Missing authorId parameter', 400); + case 'author-tweets': { + const authorId = url.searchParams.get('authorId'); + if (!authorId) { + return createApiResponse('Missing authorId parameter', 400); + } + const tweets = await getAuthorTweets(orm, authorId); + return createApiResponse({ + message: 'Successfully retrieved author tweets', + data: { tweets }, + }); } - const tweets = await getAuthorTweets(orm, authorId); - return createApiResponse({ - message: 'Successfully retrieved author tweets', - data: { tweets }, - }); - } - case 'add-tweet': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); + case 'add-tweet': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const { author_id, tweet_id, tweet_body, thread_id, parent_tweet_id, is_bot_response } = (await request.json()) as XBotTweetsTable; + if (!author_id || !tweet_id || !tweet_body) { + return createApiResponse('Missing required fields: authorId, tweetId, tweetBody', 400); + } + const tweet = await addTweet( + orm, + author_id, + tweet_id, + tweet_body, + thread_id || undefined, + parent_tweet_id || undefined, + is_bot_response || undefined + ); + return createApiResponse({ + message: 'Successfully created tweet', + data: { tweet }, + }); } - const { author_id, tweet_id, tweet_body, thread_id, parent_tweet_id, is_bot_response } = (await request.json()) as XBotTweetsTable; - if (!author_id || !tweet_id || !tweet_body) { - return createApiResponse('Missing required fields: authorId, tweetId, tweetBody', 400); - } - const tweet = await addTweet( - orm, - author_id, - tweet_id, - tweet_body, - thread_id || undefined, - parent_tweet_id || undefined, - is_bot_response || undefined - ); - return createApiResponse({ - message: 'Successfully created tweet', - data: { tweet }, - }); - } - case 'logs': { - const tweetId = url.searchParams.get('tweetId'); - if (!tweetId) { - return createApiResponse('Missing tweetId parameter', 400); + case 'logs': { + const tweetId = url.searchParams.get('tweetId'); + if (!tweetId) { + return createApiResponse('Missing tweetId parameter', 400); + } + const logs = await getTweetLogs(orm, tweetId); + return createApiResponse({ + message: 'Successfully retrieved tweet logs', + data: { logs }, + }); } - const logs = await getTweetLogs(orm, tweetId); - return createApiResponse({ - message: 'Successfully retrieved tweet logs', - data: { logs }, - }); - } - case 'add-log': { - if (request.method !== 'POST') { - return createApiResponse('Method not allowed', 405); + case 'add-log': { + if (request.method !== 'POST') { + return createApiResponse('Method not allowed', 405); + } + const { tweet_id, tweet_status, log_message } = (await request.json()) as XBotLogsTable; + if (!tweet_id || !tweet_status) { + return createApiResponse('Missing required fields: tweetId, status', 400); + } + const log = await addLog(orm, tweet_id, tweet_status, log_message || undefined); + return createApiResponse({ + message: 'Successfully created log', + data: { log }, + }); } - const { tweet_id, tweet_status, log_message } = (await request.json()) as XBotLogsTable; - if (!tweet_id || !tweet_status) { - return createApiResponse('Missing required fields: tweetId, status', 400); - } - const log = await addLog(orm, tweet_id, tweet_status, log_message || undefined); - return createApiResponse({ - message: 'Successfully created log', - data: { log }, - }); - } - default: - return createApiResponse(`Unsupported twitter endpoint: ${endpoint}`, 404); - } + default: + return createApiResponse(`Unsupported twitter endpoint: ${endpoint}`, 404); + } + }, }; diff --git a/src/database/handlers/types.ts b/src/database/handlers/types.ts index afdb52a..b2ab24e 100644 --- a/src/database/handlers/types.ts +++ b/src/database/handlers/types.ts @@ -2,22 +2,22 @@ import { D1Orm } from 'd1-orm'; import { Env } from '../../../worker-configuration'; export interface HandlerEndpoint { - path: string; - methods: string[]; - description?: string; + path: string; + methods: string[]; + description?: string; } export interface HandlerDefinition { - baseRoute: string; - endpoints: HandlerEndpoint[]; - handler: Handler; + baseRoute: string; + endpoints: HandlerEndpoint[]; + handler: Handler; } export interface HandlerContext { - orm: D1Orm; - env: Env; - request: Request; - url: URL; + orm: D1Orm; + env: Env; + request: Request; + url: URL; } export type Handler = (context: HandlerContext) => Promise; From 55b058b48fe95aac6057eb0d9e14f74dfc85d8ab Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:22:17 -0700 Subject: [PATCH 089/128] refactor: Update endpoint documentation and types for self-documenting API structure --- src/database/handlers/conversations.ts | 21 +++++++++++++++++++-- src/database/handlers/types.ts | 5 ++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/database/handlers/conversations.ts b/src/database/handlers/conversations.ts index 6514eb6..8b08114 100644 --- a/src/database/handlers/conversations.ts +++ b/src/database/handlers/conversations.ts @@ -7,24 +7,41 @@ export const conversationsHandler: HandlerDefinition = { baseRoute: 'conversations', endpoints: [ { - path: '/conversations/conversations', + path: '/conversations/list', methods: ['GET'], description: 'Get all conversations for an address', + requiresAuth: true, + parameters: { + address: 'STX address of the user' + } }, { path: '/conversations/latest', - methods: ['GET'], + methods: ['GET'], description: 'Get latest conversation for an address', + requiresAuth: true, + parameters: { + address: 'STX address of the user' + } }, { path: '/conversations/history', methods: ['GET'], description: 'Get conversation history by ID', + requiresAuth: true, + parameters: { + id: 'Conversation ID' + } }, { path: '/conversations/create', methods: ['POST'], description: 'Create a new conversation', + requiresAuth: true, + requestBody: { + profile_id: 'STX address of the user', + conversation_name: 'Optional name for the conversation' + } }, ], handler: async ({ orm, request, url }) => { diff --git a/src/database/handlers/types.ts b/src/database/handlers/types.ts index b2ab24e..f556dca 100644 --- a/src/database/handlers/types.ts +++ b/src/database/handlers/types.ts @@ -4,7 +4,10 @@ import { Env } from '../../../worker-configuration'; export interface HandlerEndpoint { path: string; methods: string[]; - description?: string; + description: string; + requiresAuth?: boolean; + parameters?: Record; + requestBody?: Record; } export interface HandlerDefinition { From fe9fb8e9823c5be2990bbd1505e3cb56a6a81991 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:23:06 -0700 Subject: [PATCH 090/128] refactor: Enhance agents and conversations handlers with authentication and documentation --- src/database/handlers/agents.ts | 39 +++++++++++++++++++++++++- src/database/handlers/conversations.ts | 4 +-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/database/handlers/agents.ts b/src/database/handlers/agents.ts index 83d6160..d96a58b 100644 --- a/src/database/handlers/agents.ts +++ b/src/database/handlers/agents.ts @@ -11,26 +11,63 @@ export const agentsHandler: HandlerDefinition = { path: '/agents/get', methods: ['GET'], description: 'Get all agents for a crew', + requiresAuth: true, + parameters: { + crewId: 'ID of the crew to get agents for' + } }, { path: '/agents/create', methods: ['POST'], description: 'Create a new agent', + requiresAuth: true, + requestBody: { + profile_id: 'STX address of the user', + crew_id: 'ID of the crew', + agent_name: 'Name of the agent', + agent_role: 'Role of the agent', + agent_goal: 'Goal of the agent', + agent_backstory: 'Backstory of the agent' + } }, { path: '/agents/update', methods: ['PUT'], description: 'Update an existing agent', + requiresAuth: true, + parameters: { + id: 'ID of the agent to update' + }, + requestBody: { + agent_name: 'Optional: New name of the agent', + agent_role: 'Optional: New role of the agent', + agent_goal: 'Optional: New goal of the agent', + agent_backstory: 'Optional: New backstory of the agent' + } }, { path: '/agents/delete', methods: ['DELETE'], description: 'Delete an agent', + requiresAuth: true, + parameters: { + id: 'ID of the agent to delete' + } }, ], handler: async ({ orm, env, request, url }) => { - const endpoint = url.pathname.split('/').pop(); + // Verify authentication for all endpoints + const authHeader = request.headers.get('Authorization'); + if (!authHeader) { + return createApiResponse('Missing authorization header', 401); + } + const token = authHeader.replace('Bearer ', ''); + const tokenAddress = await validateSessionToken(env, token); + if (!tokenAddress.success) { + return createApiResponse('Unauthorized access', 403); + } + const endpoint = url.pathname.split('/').pop(); switch (endpoint) { case 'get': { const crewId = url.searchParams.get('crewId'); diff --git a/src/database/handlers/conversations.ts b/src/database/handlers/conversations.ts index 8b08114..1546036 100644 --- a/src/database/handlers/conversations.ts +++ b/src/database/handlers/conversations.ts @@ -48,7 +48,7 @@ export const conversationsHandler: HandlerDefinition = { const endpoint = url.pathname.split('/').pop(); switch (endpoint) { - case 'conversations': + case 'list': const address = url.searchParams.get('address'); if (!address) { return createApiResponse('Missing address parameter', 400); @@ -56,7 +56,7 @@ export const conversationsHandler: HandlerDefinition = { const conversations = await getConversations(orm, address); return createApiResponse({ message: 'Successfully retrieved conversations', - data: conversations, + data: { conversations }, }); case 'latest': From 238f41ea4ef6c13b93a3c0540b40ac0961d61534 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:25:18 -0700 Subject: [PATCH 091/128] feat: Add authentication and documentation to crews handler endpoints --- src/database/handlers/crews.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/database/handlers/crews.ts b/src/database/handlers/crews.ts index be35a55..9c7cb52 100644 --- a/src/database/handlers/crews.ts +++ b/src/database/handlers/crews.ts @@ -23,21 +23,37 @@ export const crewsHandler: HandlerDefinition = { path: '/crews/profile', methods: ['GET'], description: 'Get crews for a specific profile', + requiresAuth: true, + parameters: { + address: 'STX address of the user' + } }, { path: '/crews/public', methods: ['GET'], description: 'Get all public crews', + requiresAuth: false }, { path: '/crews/get', methods: ['GET'], description: 'Get a specific crew by ID', + requiresAuth: true, + parameters: { + id: 'ID of the crew to retrieve' + } }, { path: '/crews/create', methods: ['POST'], description: 'Create a new crew', + requiresAuth: true, + requestBody: { + profile_id: 'STX address of the user', + crew_name: 'Name of the crew', + crew_description: 'Optional description of the crew', + is_public: 'Optional boolean indicating if crew is public' + } }, { path: '/crews/update', From 5aeabe1f37fd6f165e1f9ccc5db66479001843b9 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:25:36 -0700 Subject: [PATCH 092/128] feat: Add authentication and documentation to tasks handler endpoints --- src/database/handlers/tasks.ts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/database/handlers/tasks.ts b/src/database/handlers/tasks.ts index c51de86..66f34e8 100644 --- a/src/database/handlers/tasks.ts +++ b/src/database/handlers/tasks.ts @@ -11,31 +11,65 @@ export const tasksHandler: HandlerDefinition = { path: '/tasks/get', methods: ['GET'], description: 'Get a specific task by ID', + requiresAuth: true, + parameters: { + id: 'ID of the task to retrieve' + } }, { path: '/tasks/list', methods: ['GET'], description: 'Get all tasks for an agent', + requiresAuth: true, + parameters: { + agentId: 'ID of the agent to get tasks for' + } }, { path: '/tasks/create', methods: ['POST'], description: 'Create a new task', + requiresAuth: true, + requestBody: { + profile_id: 'STX address of the user', + crew_id: 'ID of the crew', + agent_id: 'ID of the agent', + task_name: 'Name of the task', + task_description: 'Description of the task', + task_expected_output: 'Expected output of the task' + } }, { path: '/tasks/update', methods: ['PUT'], description: 'Update an existing task', + requiresAuth: true, + parameters: { + id: 'ID of the task to update' + }, + requestBody: { + task_name: 'Optional: New name of the task', + task_description: 'Optional: New description of the task', + task_expected_output: 'Optional: New expected output of the task' + } }, { path: '/tasks/delete', methods: ['DELETE'], description: 'Delete a specific task', + requiresAuth: true, + parameters: { + id: 'ID of the task to delete' + } }, { path: '/tasks/delete-all', methods: ['DELETE'], description: 'Delete all tasks for an agent', + requiresAuth: true, + parameters: { + agentId: 'ID of the agent to delete all tasks for' + } }, ], handler: async ({ orm, env, request, url }) => { From 90bcccabdb9d2054cb64d3e3bfc299e51fc8250b Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:25:57 -0700 Subject: [PATCH 093/128] feat: Add authentication and detailed documentation for crons handler endpoints --- src/database/handlers/crons.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/database/handlers/crons.ts b/src/database/handlers/crons.ts index 52ea399..344883e 100644 --- a/src/database/handlers/crons.ts +++ b/src/database/handlers/crons.ts @@ -11,31 +11,59 @@ export const cronsHandler: HandlerDefinition = { path: '/crons/enabled', methods: ['GET'], description: 'Get all enabled crons', + requiresAuth: true }, { path: '/crons/enabled-detailed', methods: ['GET'], description: 'Get detailed information about enabled crons', + requiresAuth: true }, { path: '/crons/get', methods: ['GET'], description: 'Get crons for a specific crew', + requiresAuth: true, + parameters: { + crewId: 'ID of the crew to get crons for' + } }, { path: '/crons/create', methods: ['POST'], description: 'Create a new cron', + requiresAuth: true, + requestBody: { + profile_id: 'STX address of the user', + crew_id: 'ID of the crew', + cron_enabled: 'Boolean indicating if cron is enabled', + cron_interval: 'Optional: Cron schedule expression (default: "0 * * * *")', + cron_input: 'Optional: Input data for the cron job' + } }, { path: '/crons/update', methods: ['PUT'], description: 'Update cron input', + requiresAuth: true, + parameters: { + id: 'ID of the cron to update' + }, + requestBody: { + cron_input: 'New input data for the cron job' + } }, { path: '/crons/toggle', methods: ['PUT'], description: 'Toggle cron status', + requiresAuth: true, + parameters: { + id: 'ID of the cron to toggle' + }, + requestBody: { + cron_enabled: 'Boolean indicating desired cron status' + } }, ], handler: async ({ orm, env, request, url }) => { From 9bf6545ccd2bdf044addb95dd6bab204bb6c286b Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:26:13 -0700 Subject: [PATCH 094/128] feat: Add authentication and detailed documentation to profiles endpoints --- src/database/handlers/profiles.ts | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/database/handlers/profiles.ts b/src/database/handlers/profiles.ts index 9d58227..62360d1 100644 --- a/src/database/handlers/profiles.ts +++ b/src/database/handlers/profiles.ts @@ -18,36 +18,75 @@ export const profilesHandler: HandlerDefinition = { path: '/profiles/role', methods: ['GET'], description: 'Get user role by address', + requiresAuth: true, + parameters: { + address: 'STX address of the user' + } }, { path: '/profiles/get', methods: ['GET'], description: 'Get user profile by address', + requiresAuth: true, + parameters: { + address: 'STX address of the user' + } }, { path: '/profiles/create', methods: ['POST'], description: 'Create new user profile', + requiresAuth: true, + requestBody: { + stx_address: 'STX address of the user', + user_role: 'Role of the user (admin/user)', + user_name: 'Optional: Display name of the user', + user_avatar: 'Optional: Avatar URL for the user' + } }, { path: '/profiles/update', methods: ['PUT'], description: 'Update existing user profile', + requiresAuth: true, + parameters: { + address: 'STX address of the user' + }, + requestBody: { + user_role: 'Optional: New role of the user', + user_name: 'Optional: New display name', + user_avatar: 'Optional: New avatar URL' + } }, { path: '/profiles/delete', methods: ['DELETE'], description: 'Delete user profile', + requiresAuth: true, + parameters: { + address: 'STX address of the user' + } }, { path: '/profiles/list', methods: ['GET'], description: 'Get all user profiles', + requiresAuth: true }, { path: '/profiles/admin-update', methods: ['PUT'], description: 'Admin update of user profile by ID', + requiresAuth: true, + parameters: { + userId: 'ID of the user to update' + }, + requestBody: { + stx_address: 'Optional: New STX address', + user_role: 'Optional: New user role', + user_name: 'Optional: New display name', + user_avatar: 'Optional: New avatar URL' + } }, ], handler: async ({ orm, env, request, url }) => { From b1d4b0643520e6d4330756e751e5055207d3b5d7 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:26:36 -0700 Subject: [PATCH 095/128] feat: Add authentication and detailed documentation to Twitter handler endpoints --- src/database/handlers/twitter.ts | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/database/handlers/twitter.ts b/src/database/handlers/twitter.ts index 260b93c..eaf3d8b 100644 --- a/src/database/handlers/twitter.ts +++ b/src/database/handlers/twitter.ts @@ -10,41 +10,82 @@ export const twitterHandler: HandlerDefinition = { path: '/twitter/get', methods: ['GET'], description: 'Get author by ID', + requiresAuth: true, + parameters: { + authorId: 'Twitter author ID to retrieve' + } }, { path: '/twitter/create', methods: ['POST'], description: 'Create a new author', + requiresAuth: true, + requestBody: { + author_id: 'Twitter author ID', + realname: 'Optional: Real name of the author', + username: 'Optional: Twitter username' + } }, { path: '/twitter/tweet', methods: ['GET'], description: 'Get tweet by ID', + requiresAuth: true, + parameters: { + tweetId: 'Twitter tweet ID to retrieve' + } }, { path: '/twitter/thread', methods: ['GET'], description: 'Get tweets in a thread', + requiresAuth: true, + parameters: { + threadId: 'Thread ID to retrieve tweets from' + } }, { path: '/twitter/author-tweets', methods: ['GET'], description: 'Get tweets by author', + requiresAuth: true, + parameters: { + authorId: 'Twitter author ID to get tweets for' + } }, { path: '/twitter/add-tweet', methods: ['POST'], description: 'Add a new tweet', + requiresAuth: true, + requestBody: { + author_id: 'Twitter author ID', + tweet_id: 'Twitter tweet ID', + tweet_body: 'Content of the tweet', + thread_id: 'Optional: Thread ID if part of a thread', + parent_tweet_id: 'Optional: ID of parent tweet if reply', + is_bot_response: 'Optional: Boolean indicating if tweet is bot response' + } }, { path: '/twitter/logs', methods: ['GET'], description: 'Get logs for a tweet', + requiresAuth: true, + parameters: { + tweetId: 'Tweet ID to get logs for' + } }, { path: '/twitter/add-log', methods: ['POST'], description: 'Add a new log entry', + requiresAuth: true, + requestBody: { + tweet_id: 'Tweet ID to add log for', + tweet_status: 'Status of the tweet', + log_message: 'Optional: Additional log message' + } }, ], handler: async ({ orm, request, url }) => { From cb017be199e1adece798fb17ad4c16b482412a87 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:27:38 -0700 Subject: [PATCH 096/128] refactor: Update crews handler endpoint documentation --- src/database/handlers/crews.ts | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/database/handlers/crews.ts b/src/database/handlers/crews.ts index 9c7cb52..24c7618 100644 --- a/src/database/handlers/crews.ts +++ b/src/database/handlers/crews.ts @@ -59,36 +59,76 @@ export const crewsHandler: HandlerDefinition = { path: '/crews/update', methods: ['PUT'], description: 'Update an existing crew', + requiresAuth: true, + parameters: { + id: 'ID of the crew to update' + }, + requestBody: { + crew_name: 'Optional: New name of the crew', + crew_description: 'Optional: New description of the crew', + is_public: 'Optional: Boolean indicating if crew should be public' + } }, { path: '/crews/delete', methods: ['DELETE'], description: 'Delete a crew', + requiresAuth: true, + parameters: { + id: 'ID of the crew to delete' + } }, { path: '/crews/executions', methods: ['GET'], description: 'Get crew executions for an address', + requiresAuth: true, + parameters: { + address: 'STX address to get executions for' + } }, { path: '/crews/executions/add', methods: ['POST'], description: 'Add a new crew execution', + requiresAuth: true, + requestBody: { + address: 'STX address of the user', + crewId: 'ID of the crew', + conversationId: 'ID of the conversation', + input: 'Input data for the execution' + } }, { path: '/crews/steps/get', methods: ['GET'], description: 'Get execution steps', + requiresAuth: true, + parameters: { + executionId: 'ID of the execution to get steps for' + } }, { path: '/crews/steps/create', methods: ['POST'], description: 'Create a new execution step', + requiresAuth: true, + requestBody: { + profile_id: 'STX address of the user', + crew_id: 'ID of the crew', + execution_id: 'ID of the execution', + step_type: 'Type of execution step', + step_data: 'Data for the execution step' + } }, { path: '/crews/steps/delete', methods: ['DELETE'], description: 'Delete execution steps', + requiresAuth: true, + parameters: { + executionId: 'ID of the execution to delete steps for' + } }, ], handler: async ({ orm, env, request, url }) => { From 25cae21040c96eec4b1aa5340bb45cc3ea7704e0 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:28:42 -0700 Subject: [PATCH 097/128] refactor: Update request body parameters to use camelCase --- src/database/handlers/agents.ts | 20 ++++++++++---------- src/database/handlers/conversations.ts | 4 ++-- src/database/handlers/crons.ts | 14 +++++++------- src/database/handlers/profiles.ts | 22 +++++++++++----------- src/database/handlers/twitter.ts | 22 +++++++++++----------- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/database/handlers/agents.ts b/src/database/handlers/agents.ts index d96a58b..3ab29e6 100644 --- a/src/database/handlers/agents.ts +++ b/src/database/handlers/agents.ts @@ -22,12 +22,12 @@ export const agentsHandler: HandlerDefinition = { description: 'Create a new agent', requiresAuth: true, requestBody: { - profile_id: 'STX address of the user', - crew_id: 'ID of the crew', - agent_name: 'Name of the agent', - agent_role: 'Role of the agent', - agent_goal: 'Goal of the agent', - agent_backstory: 'Backstory of the agent' + profileId: 'STX address of the user', + crewId: 'ID of the crew', + agentName: 'Name of the agent', + agentRole: 'Role of the agent', + agentGoal: 'Goal of the agent', + agentBackstory: 'Backstory of the agent' } }, { @@ -39,10 +39,10 @@ export const agentsHandler: HandlerDefinition = { id: 'ID of the agent to update' }, requestBody: { - agent_name: 'Optional: New name of the agent', - agent_role: 'Optional: New role of the agent', - agent_goal: 'Optional: New goal of the agent', - agent_backstory: 'Optional: New backstory of the agent' + agentName: 'Optional: New name of the agent', + agentRole: 'Optional: New role of the agent', + agentGoal: 'Optional: New goal of the agent', + agentBackstory: 'Optional: New backstory of the agent' } }, { diff --git a/src/database/handlers/conversations.ts b/src/database/handlers/conversations.ts index 1546036..8afbe30 100644 --- a/src/database/handlers/conversations.ts +++ b/src/database/handlers/conversations.ts @@ -39,8 +39,8 @@ export const conversationsHandler: HandlerDefinition = { description: 'Create a new conversation', requiresAuth: true, requestBody: { - profile_id: 'STX address of the user', - conversation_name: 'Optional name for the conversation' + profileId: 'STX address of the user', + conversationName: 'Optional name for the conversation' } }, ], diff --git a/src/database/handlers/crons.ts b/src/database/handlers/crons.ts index 344883e..71ba1d5 100644 --- a/src/database/handlers/crons.ts +++ b/src/database/handlers/crons.ts @@ -34,11 +34,11 @@ export const cronsHandler: HandlerDefinition = { description: 'Create a new cron', requiresAuth: true, requestBody: { - profile_id: 'STX address of the user', - crew_id: 'ID of the crew', - cron_enabled: 'Boolean indicating if cron is enabled', - cron_interval: 'Optional: Cron schedule expression (default: "0 * * * *")', - cron_input: 'Optional: Input data for the cron job' + profileId: 'STX address of the user', + crewId: 'ID of the crew', + cronEnabled: 'Boolean indicating if cron is enabled', + cronInterval: 'Optional: Cron schedule expression (default: "0 * * * *")', + cronInput: 'Optional: Input data for the cron job' } }, { @@ -50,7 +50,7 @@ export const cronsHandler: HandlerDefinition = { id: 'ID of the cron to update' }, requestBody: { - cron_input: 'New input data for the cron job' + cronInput: 'New input data for the cron job' } }, { @@ -62,7 +62,7 @@ export const cronsHandler: HandlerDefinition = { id: 'ID of the cron to toggle' }, requestBody: { - cron_enabled: 'Boolean indicating desired cron status' + cronEnabled: 'Boolean indicating desired cron status' } }, ], diff --git a/src/database/handlers/profiles.ts b/src/database/handlers/profiles.ts index 62360d1..28b807c 100644 --- a/src/database/handlers/profiles.ts +++ b/src/database/handlers/profiles.ts @@ -38,10 +38,10 @@ export const profilesHandler: HandlerDefinition = { description: 'Create new user profile', requiresAuth: true, requestBody: { - stx_address: 'STX address of the user', - user_role: 'Role of the user (admin/user)', - user_name: 'Optional: Display name of the user', - user_avatar: 'Optional: Avatar URL for the user' + stxAddress: 'STX address of the user', + userRole: 'Role of the user (admin/user)', + userName: 'Optional: Display name of the user', + userAvatar: 'Optional: Avatar URL for the user' } }, { @@ -53,9 +53,9 @@ export const profilesHandler: HandlerDefinition = { address: 'STX address of the user' }, requestBody: { - user_role: 'Optional: New role of the user', - user_name: 'Optional: New display name', - user_avatar: 'Optional: New avatar URL' + userRole: 'Optional: New role of the user', + userName: 'Optional: New display name', + userAvatar: 'Optional: New avatar URL' } }, { @@ -82,10 +82,10 @@ export const profilesHandler: HandlerDefinition = { userId: 'ID of the user to update' }, requestBody: { - stx_address: 'Optional: New STX address', - user_role: 'Optional: New user role', - user_name: 'Optional: New display name', - user_avatar: 'Optional: New avatar URL' + stxAddress: 'Optional: New STX address', + userRole: 'Optional: New user role', + userName: 'Optional: New display name', + userAvatar: 'Optional: New avatar URL' } }, ], diff --git a/src/database/handlers/twitter.ts b/src/database/handlers/twitter.ts index eaf3d8b..32c5750 100644 --- a/src/database/handlers/twitter.ts +++ b/src/database/handlers/twitter.ts @@ -21,8 +21,8 @@ export const twitterHandler: HandlerDefinition = { description: 'Create a new author', requiresAuth: true, requestBody: { - author_id: 'Twitter author ID', - realname: 'Optional: Real name of the author', + authorId: 'Twitter author ID', + realName: 'Optional: Real name of the author', username: 'Optional: Twitter username' } }, @@ -59,12 +59,12 @@ export const twitterHandler: HandlerDefinition = { description: 'Add a new tweet', requiresAuth: true, requestBody: { - author_id: 'Twitter author ID', - tweet_id: 'Twitter tweet ID', - tweet_body: 'Content of the tweet', - thread_id: 'Optional: Thread ID if part of a thread', - parent_tweet_id: 'Optional: ID of parent tweet if reply', - is_bot_response: 'Optional: Boolean indicating if tweet is bot response' + authorId: 'Twitter author ID', + tweetId: 'Twitter tweet ID', + tweetBody: 'Content of the tweet', + threadId: 'Optional: Thread ID if part of a thread', + parentTweetId: 'Optional: ID of parent tweet if reply', + isBotResponse: 'Optional: Boolean indicating if tweet is bot response' } }, { @@ -82,9 +82,9 @@ export const twitterHandler: HandlerDefinition = { description: 'Add a new log entry', requiresAuth: true, requestBody: { - tweet_id: 'Tweet ID to add log for', - tweet_status: 'Status of the tweet', - log_message: 'Optional: Additional log message' + tweetId: 'Tweet ID to add log for', + tweetStatus: 'Status of the tweet', + logMessage: 'Optional: Additional log message' } }, ], From 02d22cf264316656c1f1a01a9743f877f2d87801 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 13:29:38 -0700 Subject: [PATCH 098/128] refactor: Convert request body parameters to camelCase across handlers --- src/database/handlers/conversations.ts | 6 +++--- src/database/handlers/crons.ts | 24 +++++++++++----------- src/database/handlers/profiles.ts | 4 ++-- src/database/handlers/twitter.ts | 28 +++++++++++++------------- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/database/handlers/conversations.ts b/src/database/handlers/conversations.ts index 8afbe30..687402f 100644 --- a/src/database/handlers/conversations.ts +++ b/src/database/handlers/conversations.ts @@ -85,11 +85,11 @@ export const conversationsHandler: HandlerDefinition = { if (request.method !== 'POST') { return createApiResponse('Method not allowed', 405); } - const { profile_id, conversation_name } = (await request.json()) as UserConversationsTable; - if (!profile_id) { + const { profileId, conversationName } = (await request.json()) as UserConversationsTable; + if (!profileId) { return createApiResponse('Missing required field: address', 400); } - const result = await addConversation(orm, profile_id, conversation_name ? conversation_name : 'new conversation'); + const result = await addConversation(orm, profileId, conversationName ? conversationName : 'new conversation'); return createApiResponse({ message: 'Successfully created conversation', data: { result }, diff --git a/src/database/handlers/crons.ts b/src/database/handlers/crons.ts index 71ba1d5..ad47e25 100644 --- a/src/database/handlers/crons.ts +++ b/src/database/handlers/crons.ts @@ -103,12 +103,12 @@ export const cronsHandler: HandlerDefinition = { return createApiResponse('Method not allowed', 405); } const cronData = (await request.json()) as UserCronsTable; - if (!cronData.profile_id || !cronData.crew_id || cronData.cron_enabled === undefined) { - return createApiResponse('Missing required fields: profile_id, crew_id, cron_enabled', 400); + if (!cronData.profileId || !cronData.crewId || cronData.cronEnabled === undefined) { + return createApiResponse('Missing required fields: profileId, crewId, cronEnabled', 400); } // Set defaults if not provided - cronData.cron_interval = cronData.cron_interval || '0 * * * *'; // Default to hourly - cronData.cron_input = cronData.cron_input || ''; + cronData.cronInterval = cronData.cronInterval || '0 * * * *'; // Default to hourly + cronData.cronInput = cronData.cronInput || ''; const cron = await createCron(orm, cronData); return createApiResponse({ message: 'Successfully created cron', @@ -124,11 +124,11 @@ export const cronsHandler: HandlerDefinition = { if (!cronId) { return createApiResponse('Missing id parameter', 400); } - const { cron_input } = (await request.json()) as UserCronsTable; - if (cron_input === undefined) { - return createApiResponse('Missing cron_input in request body', 400); + const { cronInput } = (await request.json()) as UserCronsTable; + if (cronInput === undefined) { + return createApiResponse('Missing cronInput in request body', 400); } - const result = await updateCronInput(orm, parseInt(cronId), cron_input); + const result = await updateCronInput(orm, parseInt(cronId), cronInput); return createApiResponse({ message: 'Successfully updated cron input', data: { result }, @@ -143,11 +143,11 @@ export const cronsHandler: HandlerDefinition = { if (!cronId) { return createApiResponse('Missing id parameter', 400); } - const { cron_enabled } = (await request.json()) as UserCronsTable; - if (cron_enabled === undefined) { - return createApiResponse('Missing cron_enabled in request body', 400); + const { cronEnabled } = (await request.json()) as UserCronsTable; + if (cronEnabled === undefined) { + return createApiResponse('Missing cronEnabled in request body', 400); } - const result = await toggleCronStatus(orm, parseInt(cronId), cron_enabled ? 1 : 0); + const result = await toggleCronStatus(orm, parseInt(cronId), cronEnabled ? 1 : 0); return createApiResponse({ message: 'Successfully toggled cron status', data: { result }, diff --git a/src/database/handlers/profiles.ts b/src/database/handlers/profiles.ts index 28b807c..0be3391 100644 --- a/src/database/handlers/profiles.ts +++ b/src/database/handlers/profiles.ts @@ -122,8 +122,8 @@ export const profilesHandler: HandlerDefinition = { return createApiResponse('Method not allowed', 405); } const profileData = (await request.json()) as UserProfilesTable; - if (!profileData.stx_address || !profileData.user_role) { - return createApiResponse('Missing required fields: stx_address, user_role', 400); + if (!profileData.stxAddress || !profileData.userRole) { + return createApiResponse('Missing required fields: stxAddress, userRole', 400); } const profile = await createUserProfile(orm, profileData); return createApiResponse({ diff --git a/src/database/handlers/twitter.ts b/src/database/handlers/twitter.ts index 32c5750..4a657f1 100644 --- a/src/database/handlers/twitter.ts +++ b/src/database/handlers/twitter.ts @@ -108,11 +108,11 @@ export const twitterHandler: HandlerDefinition = { if (request.method !== 'POST') { return createApiResponse('Method not allowed', 405); } - const { author_id, realname, username } = (await request.json()) as XBotAuthorsTable; - if (!author_id) { + const { authorId, realName, username } = (await request.json()) as XBotAuthorsTable; + if (!authorId) { return createApiResponse('Missing required fields: authorId', 400); } - const author = await addAuthor(orm, author_id, realname || undefined, username || undefined); + const author = await addAuthor(orm, authorId, realName || undefined, username || undefined); return createApiResponse({ message: 'Successfully created author', data: { author }, @@ -159,18 +159,18 @@ export const twitterHandler: HandlerDefinition = { if (request.method !== 'POST') { return createApiResponse('Method not allowed', 405); } - const { author_id, tweet_id, tweet_body, thread_id, parent_tweet_id, is_bot_response } = (await request.json()) as XBotTweetsTable; - if (!author_id || !tweet_id || !tweet_body) { + const { authorId, tweetId, tweetBody, threadId, parentTweetId, isBotResponse } = (await request.json()) as XBotTweetsTable; + if (!authorId || !tweetId || !tweetBody) { return createApiResponse('Missing required fields: authorId, tweetId, tweetBody', 400); } const tweet = await addTweet( orm, - author_id, - tweet_id, - tweet_body, - thread_id || undefined, - parent_tweet_id || undefined, - is_bot_response || undefined + authorId, + tweetId, + tweetBody, + threadId || undefined, + parentTweetId || undefined, + isBotResponse || undefined ); return createApiResponse({ message: 'Successfully created tweet', @@ -194,11 +194,11 @@ export const twitterHandler: HandlerDefinition = { if (request.method !== 'POST') { return createApiResponse('Method not allowed', 405); } - const { tweet_id, tweet_status, log_message } = (await request.json()) as XBotLogsTable; - if (!tweet_id || !tweet_status) { + const { tweetId, tweetStatus, logMessage } = (await request.json()) as XBotLogsTable; + if (!tweetId || !tweetStatus) { return createApiResponse('Missing required fields: tweetId, status', 400); } - const log = await addLog(orm, tweet_id, tweet_status, log_message || undefined); + const log = await addLog(orm, tweetId, tweetStatus, logMessage || undefined); return createApiResponse({ message: 'Successfully created log', data: { log }, From cf38aa274bd77ebbb2211480f91bfb0fea518284 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Mon, 23 Dec 2024 13:33:05 -0700 Subject: [PATCH 099/128] style: Format agents handler with consistent trailing commas --- src/database/handlers/agents.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/database/handlers/agents.ts b/src/database/handlers/agents.ts index 3ab29e6..4f76585 100644 --- a/src/database/handlers/agents.ts +++ b/src/database/handlers/agents.ts @@ -13,8 +13,8 @@ export const agentsHandler: HandlerDefinition = { description: 'Get all agents for a crew', requiresAuth: true, parameters: { - crewId: 'ID of the crew to get agents for' - } + crewId: 'ID of the crew to get agents for', + }, }, { path: '/agents/create', @@ -27,8 +27,8 @@ export const agentsHandler: HandlerDefinition = { agentName: 'Name of the agent', agentRole: 'Role of the agent', agentGoal: 'Goal of the agent', - agentBackstory: 'Backstory of the agent' - } + agentBackstory: 'Backstory of the agent', + }, }, { path: '/agents/update', @@ -36,14 +36,14 @@ export const agentsHandler: HandlerDefinition = { description: 'Update an existing agent', requiresAuth: true, parameters: { - id: 'ID of the agent to update' + id: 'ID of the agent to update', }, requestBody: { agentName: 'Optional: New name of the agent', agentRole: 'Optional: New role of the agent', agentGoal: 'Optional: New goal of the agent', - agentBackstory: 'Optional: New backstory of the agent' - } + agentBackstory: 'Optional: New backstory of the agent', + }, }, { path: '/agents/delete', @@ -51,8 +51,8 @@ export const agentsHandler: HandlerDefinition = { description: 'Delete an agent', requiresAuth: true, parameters: { - id: 'ID of the agent to delete' - } + id: 'ID of the agent to delete', + }, }, ], handler: async ({ orm, env, request, url }) => { From 50027d96808c859e0d1d081edcee6c60d9ad7125 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Mon, 23 Dec 2024 15:24:14 -0700 Subject: [PATCH 100/128] human intervention: code cleanup --- src/database/handlers/conversations.ts | 20 +++++------ src/database/handlers/crews.ts | 44 ++++++++++++------------ src/database/handlers/crons.ts | 34 +++++++++---------- src/database/handlers/profiles.ts | 32 +++++++++--------- src/database/handlers/tasks.ts | 26 +++++++------- src/database/handlers/twitter.ts | 47 +++++++++++++++----------- 6 files changed, 105 insertions(+), 98 deletions(-) diff --git a/src/database/handlers/conversations.ts b/src/database/handlers/conversations.ts index 687402f..5929f3d 100644 --- a/src/database/handlers/conversations.ts +++ b/src/database/handlers/conversations.ts @@ -12,17 +12,17 @@ export const conversationsHandler: HandlerDefinition = { description: 'Get all conversations for an address', requiresAuth: true, parameters: { - address: 'STX address of the user' - } + address: 'STX address of the user', + }, }, { path: '/conversations/latest', - methods: ['GET'], + methods: ['GET'], description: 'Get latest conversation for an address', requiresAuth: true, parameters: { - address: 'STX address of the user' - } + address: 'STX address of the user', + }, }, { path: '/conversations/history', @@ -30,8 +30,8 @@ export const conversationsHandler: HandlerDefinition = { description: 'Get conversation history by ID', requiresAuth: true, parameters: { - id: 'Conversation ID' - } + id: 'Conversation ID', + }, }, { path: '/conversations/create', @@ -40,8 +40,8 @@ export const conversationsHandler: HandlerDefinition = { requiresAuth: true, requestBody: { profileId: 'STX address of the user', - conversationName: 'Optional name for the conversation' - } + conversationName: 'Optional name for the conversation', + }, }, ], handler: async ({ orm, request, url }) => { @@ -85,7 +85,7 @@ export const conversationsHandler: HandlerDefinition = { if (request.method !== 'POST') { return createApiResponse('Method not allowed', 405); } - const { profileId, conversationName } = (await request.json()) as UserConversationsTable; + const { profile_id: profileId, conversation_name: conversationName } = (await request.json()) as UserConversationsTable; if (!profileId) { return createApiResponse('Missing required field: address', 400); } diff --git a/src/database/handlers/crews.ts b/src/database/handlers/crews.ts index 24c7618..091e4ee 100644 --- a/src/database/handlers/crews.ts +++ b/src/database/handlers/crews.ts @@ -25,14 +25,14 @@ export const crewsHandler: HandlerDefinition = { description: 'Get crews for a specific profile', requiresAuth: true, parameters: { - address: 'STX address of the user' - } + address: 'STX address of the user', + }, }, { path: '/crews/public', methods: ['GET'], description: 'Get all public crews', - requiresAuth: false + requiresAuth: false, }, { path: '/crews/get', @@ -40,8 +40,8 @@ export const crewsHandler: HandlerDefinition = { description: 'Get a specific crew by ID', requiresAuth: true, parameters: { - id: 'ID of the crew to retrieve' - } + id: 'ID of the crew to retrieve', + }, }, { path: '/crews/create', @@ -52,8 +52,8 @@ export const crewsHandler: HandlerDefinition = { profile_id: 'STX address of the user', crew_name: 'Name of the crew', crew_description: 'Optional description of the crew', - is_public: 'Optional boolean indicating if crew is public' - } + is_public: 'Optional boolean indicating if crew is public', + }, }, { path: '/crews/update', @@ -61,13 +61,13 @@ export const crewsHandler: HandlerDefinition = { description: 'Update an existing crew', requiresAuth: true, parameters: { - id: 'ID of the crew to update' + id: 'ID of the crew to update', }, requestBody: { crew_name: 'Optional: New name of the crew', crew_description: 'Optional: New description of the crew', - is_public: 'Optional: Boolean indicating if crew should be public' - } + is_public: 'Optional: Boolean indicating if crew should be public', + }, }, { path: '/crews/delete', @@ -75,8 +75,8 @@ export const crewsHandler: HandlerDefinition = { description: 'Delete a crew', requiresAuth: true, parameters: { - id: 'ID of the crew to delete' - } + id: 'ID of the crew to delete', + }, }, { path: '/crews/executions', @@ -84,8 +84,8 @@ export const crewsHandler: HandlerDefinition = { description: 'Get crew executions for an address', requiresAuth: true, parameters: { - address: 'STX address to get executions for' - } + address: 'STX address to get executions for', + }, }, { path: '/crews/executions/add', @@ -96,8 +96,8 @@ export const crewsHandler: HandlerDefinition = { address: 'STX address of the user', crewId: 'ID of the crew', conversationId: 'ID of the conversation', - input: 'Input data for the execution' - } + input: 'Input data for the execution', + }, }, { path: '/crews/steps/get', @@ -105,8 +105,8 @@ export const crewsHandler: HandlerDefinition = { description: 'Get execution steps', requiresAuth: true, parameters: { - executionId: 'ID of the execution to get steps for' - } + executionId: 'ID of the execution to get steps for', + }, }, { path: '/crews/steps/create', @@ -118,8 +118,8 @@ export const crewsHandler: HandlerDefinition = { crew_id: 'ID of the crew', execution_id: 'ID of the execution', step_type: 'Type of execution step', - step_data: 'Data for the execution step' - } + step_data: 'Data for the execution step', + }, }, { path: '/crews/steps/delete', @@ -127,8 +127,8 @@ export const crewsHandler: HandlerDefinition = { description: 'Delete execution steps', requiresAuth: true, parameters: { - executionId: 'ID of the execution to delete steps for' - } + executionId: 'ID of the execution to delete steps for', + }, }, ], handler: async ({ orm, env, request, url }) => { diff --git a/src/database/handlers/crons.ts b/src/database/handlers/crons.ts index ad47e25..4b53a43 100644 --- a/src/database/handlers/crons.ts +++ b/src/database/handlers/crons.ts @@ -11,13 +11,13 @@ export const cronsHandler: HandlerDefinition = { path: '/crons/enabled', methods: ['GET'], description: 'Get all enabled crons', - requiresAuth: true + requiresAuth: true, }, { path: '/crons/enabled-detailed', methods: ['GET'], description: 'Get detailed information about enabled crons', - requiresAuth: true + requiresAuth: true, }, { path: '/crons/get', @@ -25,8 +25,8 @@ export const cronsHandler: HandlerDefinition = { description: 'Get crons for a specific crew', requiresAuth: true, parameters: { - crewId: 'ID of the crew to get crons for' - } + crewId: 'ID of the crew to get crons for', + }, }, { path: '/crons/create', @@ -38,8 +38,8 @@ export const cronsHandler: HandlerDefinition = { crewId: 'ID of the crew', cronEnabled: 'Boolean indicating if cron is enabled', cronInterval: 'Optional: Cron schedule expression (default: "0 * * * *")', - cronInput: 'Optional: Input data for the cron job' - } + cronInput: 'Optional: Input data for the cron job', + }, }, { path: '/crons/update', @@ -47,11 +47,11 @@ export const cronsHandler: HandlerDefinition = { description: 'Update cron input', requiresAuth: true, parameters: { - id: 'ID of the cron to update' + id: 'ID of the cron to update', }, requestBody: { - cronInput: 'New input data for the cron job' - } + cronInput: 'New input data for the cron job', + }, }, { path: '/crons/toggle', @@ -59,11 +59,11 @@ export const cronsHandler: HandlerDefinition = { description: 'Toggle cron status', requiresAuth: true, parameters: { - id: 'ID of the cron to toggle' + id: 'ID of the cron to toggle', }, requestBody: { - cronEnabled: 'Boolean indicating desired cron status' - } + cronEnabled: 'Boolean indicating desired cron status', + }, }, ], handler: async ({ orm, env, request, url }) => { @@ -103,12 +103,12 @@ export const cronsHandler: HandlerDefinition = { return createApiResponse('Method not allowed', 405); } const cronData = (await request.json()) as UserCronsTable; - if (!cronData.profileId || !cronData.crewId || cronData.cronEnabled === undefined) { + if (!cronData.profile_id || !cronData.crew_id || cronData.cron_enabled === undefined) { return createApiResponse('Missing required fields: profileId, crewId, cronEnabled', 400); } // Set defaults if not provided - cronData.cronInterval = cronData.cronInterval || '0 * * * *'; // Default to hourly - cronData.cronInput = cronData.cronInput || ''; + cronData.cron_interval = cronData.cron_interval || '0 * * * *'; // Default to hourly + cronData.cron_input = cronData.cron_input || ''; const cron = await createCron(orm, cronData); return createApiResponse({ message: 'Successfully created cron', @@ -124,7 +124,7 @@ export const cronsHandler: HandlerDefinition = { if (!cronId) { return createApiResponse('Missing id parameter', 400); } - const { cronInput } = (await request.json()) as UserCronsTable; + const { cron_input: cronInput } = (await request.json()) as UserCronsTable; if (cronInput === undefined) { return createApiResponse('Missing cronInput in request body', 400); } @@ -143,7 +143,7 @@ export const cronsHandler: HandlerDefinition = { if (!cronId) { return createApiResponse('Missing id parameter', 400); } - const { cronEnabled } = (await request.json()) as UserCronsTable; + const { cron_enabled: cronEnabled } = (await request.json()) as UserCronsTable; if (cronEnabled === undefined) { return createApiResponse('Missing cronEnabled in request body', 400); } diff --git a/src/database/handlers/profiles.ts b/src/database/handlers/profiles.ts index 0be3391..b521c91 100644 --- a/src/database/handlers/profiles.ts +++ b/src/database/handlers/profiles.ts @@ -20,8 +20,8 @@ export const profilesHandler: HandlerDefinition = { description: 'Get user role by address', requiresAuth: true, parameters: { - address: 'STX address of the user' - } + address: 'STX address of the user', + }, }, { path: '/profiles/get', @@ -29,8 +29,8 @@ export const profilesHandler: HandlerDefinition = { description: 'Get user profile by address', requiresAuth: true, parameters: { - address: 'STX address of the user' - } + address: 'STX address of the user', + }, }, { path: '/profiles/create', @@ -41,8 +41,8 @@ export const profilesHandler: HandlerDefinition = { stxAddress: 'STX address of the user', userRole: 'Role of the user (admin/user)', userName: 'Optional: Display name of the user', - userAvatar: 'Optional: Avatar URL for the user' - } + userAvatar: 'Optional: Avatar URL for the user', + }, }, { path: '/profiles/update', @@ -50,13 +50,13 @@ export const profilesHandler: HandlerDefinition = { description: 'Update existing user profile', requiresAuth: true, parameters: { - address: 'STX address of the user' + address: 'STX address of the user', }, requestBody: { userRole: 'Optional: New role of the user', userName: 'Optional: New display name', - userAvatar: 'Optional: New avatar URL' - } + userAvatar: 'Optional: New avatar URL', + }, }, { path: '/profiles/delete', @@ -64,14 +64,14 @@ export const profilesHandler: HandlerDefinition = { description: 'Delete user profile', requiresAuth: true, parameters: { - address: 'STX address of the user' - } + address: 'STX address of the user', + }, }, { path: '/profiles/list', methods: ['GET'], description: 'Get all user profiles', - requiresAuth: true + requiresAuth: true, }, { path: '/profiles/admin-update', @@ -79,14 +79,14 @@ export const profilesHandler: HandlerDefinition = { description: 'Admin update of user profile by ID', requiresAuth: true, parameters: { - userId: 'ID of the user to update' + userId: 'ID of the user to update', }, requestBody: { stxAddress: 'Optional: New STX address', userRole: 'Optional: New user role', userName: 'Optional: New display name', - userAvatar: 'Optional: New avatar URL' - } + userAvatar: 'Optional: New avatar URL', + }, }, ], handler: async ({ orm, env, request, url }) => { @@ -122,7 +122,7 @@ export const profilesHandler: HandlerDefinition = { return createApiResponse('Method not allowed', 405); } const profileData = (await request.json()) as UserProfilesTable; - if (!profileData.stxAddress || !profileData.userRole) { + if (!profileData.stx_address || !profileData.user_role) { return createApiResponse('Missing required fields: stxAddress, userRole', 400); } const profile = await createUserProfile(orm, profileData); diff --git a/src/database/handlers/tasks.ts b/src/database/handlers/tasks.ts index 66f34e8..1aaeda2 100644 --- a/src/database/handlers/tasks.ts +++ b/src/database/handlers/tasks.ts @@ -13,8 +13,8 @@ export const tasksHandler: HandlerDefinition = { description: 'Get a specific task by ID', requiresAuth: true, parameters: { - id: 'ID of the task to retrieve' - } + id: 'ID of the task to retrieve', + }, }, { path: '/tasks/list', @@ -22,8 +22,8 @@ export const tasksHandler: HandlerDefinition = { description: 'Get all tasks for an agent', requiresAuth: true, parameters: { - agentId: 'ID of the agent to get tasks for' - } + agentId: 'ID of the agent to get tasks for', + }, }, { path: '/tasks/create', @@ -36,8 +36,8 @@ export const tasksHandler: HandlerDefinition = { agent_id: 'ID of the agent', task_name: 'Name of the task', task_description: 'Description of the task', - task_expected_output: 'Expected output of the task' - } + task_expected_output: 'Expected output of the task', + }, }, { path: '/tasks/update', @@ -45,13 +45,13 @@ export const tasksHandler: HandlerDefinition = { description: 'Update an existing task', requiresAuth: true, parameters: { - id: 'ID of the task to update' + id: 'ID of the task to update', }, requestBody: { task_name: 'Optional: New name of the task', task_description: 'Optional: New description of the task', - task_expected_output: 'Optional: New expected output of the task' - } + task_expected_output: 'Optional: New expected output of the task', + }, }, { path: '/tasks/delete', @@ -59,8 +59,8 @@ export const tasksHandler: HandlerDefinition = { description: 'Delete a specific task', requiresAuth: true, parameters: { - id: 'ID of the task to delete' - } + id: 'ID of the task to delete', + }, }, { path: '/tasks/delete-all', @@ -68,8 +68,8 @@ export const tasksHandler: HandlerDefinition = { description: 'Delete all tasks for an agent', requiresAuth: true, parameters: { - agentId: 'ID of the agent to delete all tasks for' - } + agentId: 'ID of the agent to delete all tasks for', + }, }, ], handler: async ({ orm, env, request, url }) => { diff --git a/src/database/handlers/twitter.ts b/src/database/handlers/twitter.ts index 4a657f1..a378e0d 100644 --- a/src/database/handlers/twitter.ts +++ b/src/database/handlers/twitter.ts @@ -12,8 +12,8 @@ export const twitterHandler: HandlerDefinition = { description: 'Get author by ID', requiresAuth: true, parameters: { - authorId: 'Twitter author ID to retrieve' - } + authorId: 'Twitter author ID to retrieve', + }, }, { path: '/twitter/create', @@ -23,8 +23,8 @@ export const twitterHandler: HandlerDefinition = { requestBody: { authorId: 'Twitter author ID', realName: 'Optional: Real name of the author', - username: 'Optional: Twitter username' - } + username: 'Optional: Twitter username', + }, }, { path: '/twitter/tweet', @@ -32,8 +32,8 @@ export const twitterHandler: HandlerDefinition = { description: 'Get tweet by ID', requiresAuth: true, parameters: { - tweetId: 'Twitter tweet ID to retrieve' - } + tweetId: 'Twitter tweet ID to retrieve', + }, }, { path: '/twitter/thread', @@ -41,8 +41,8 @@ export const twitterHandler: HandlerDefinition = { description: 'Get tweets in a thread', requiresAuth: true, parameters: { - threadId: 'Thread ID to retrieve tweets from' - } + threadId: 'Thread ID to retrieve tweets from', + }, }, { path: '/twitter/author-tweets', @@ -50,8 +50,8 @@ export const twitterHandler: HandlerDefinition = { description: 'Get tweets by author', requiresAuth: true, parameters: { - authorId: 'Twitter author ID to get tweets for' - } + authorId: 'Twitter author ID to get tweets for', + }, }, { path: '/twitter/add-tweet', @@ -64,8 +64,8 @@ export const twitterHandler: HandlerDefinition = { tweetBody: 'Content of the tweet', threadId: 'Optional: Thread ID if part of a thread', parentTweetId: 'Optional: ID of parent tweet if reply', - isBotResponse: 'Optional: Boolean indicating if tweet is bot response' - } + isBotResponse: 'Optional: Boolean indicating if tweet is bot response', + }, }, { path: '/twitter/logs', @@ -73,8 +73,8 @@ export const twitterHandler: HandlerDefinition = { description: 'Get logs for a tweet', requiresAuth: true, parameters: { - tweetId: 'Tweet ID to get logs for' - } + tweetId: 'Tweet ID to get logs for', + }, }, { path: '/twitter/add-log', @@ -84,8 +84,8 @@ export const twitterHandler: HandlerDefinition = { requestBody: { tweetId: 'Tweet ID to add log for', tweetStatus: 'Status of the tweet', - logMessage: 'Optional: Additional log message' - } + logMessage: 'Optional: Additional log message', + }, }, ], handler: async ({ orm, request, url }) => { @@ -108,11 +108,11 @@ export const twitterHandler: HandlerDefinition = { if (request.method !== 'POST') { return createApiResponse('Method not allowed', 405); } - const { authorId, realName, username } = (await request.json()) as XBotAuthorsTable; + const { author_id: authorId, realname, username } = (await request.json()) as XBotAuthorsTable; if (!authorId) { return createApiResponse('Missing required fields: authorId', 400); } - const author = await addAuthor(orm, authorId, realName || undefined, username || undefined); + const author = await addAuthor(orm, authorId, realname || undefined, username || undefined); return createApiResponse({ message: 'Successfully created author', data: { author }, @@ -159,7 +159,14 @@ export const twitterHandler: HandlerDefinition = { if (request.method !== 'POST') { return createApiResponse('Method not allowed', 405); } - const { authorId, tweetId, tweetBody, threadId, parentTweetId, isBotResponse } = (await request.json()) as XBotTweetsTable; + const { + author_id: authorId, + tweet_id: tweetId, + tweet_body: tweetBody, + thread_id: threadId, + parent_tweet_id: parentTweetId, + is_bot_response: isBotResponse, + } = (await request.json()) as XBotTweetsTable; if (!authorId || !tweetId || !tweetBody) { return createApiResponse('Missing required fields: authorId, tweetId, tweetBody', 400); } @@ -194,7 +201,7 @@ export const twitterHandler: HandlerDefinition = { if (request.method !== 'POST') { return createApiResponse('Method not allowed', 405); } - const { tweetId, tweetStatus, logMessage } = (await request.json()) as XBotLogsTable; + const { tweet_id: tweetId, tweet_status: tweetStatus, log_message: logMessage } = (await request.json()) as XBotLogsTable; if (!tweetId || !tweetStatus) { return createApiResponse('Missing required fields: tweetId, status', 400); } From 03d217890c2ca32547aeeab103d32b6973a41e74 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:27:59 -0700 Subject: [PATCH 101/128] refactor: Add camelCase transformation for UserAgents model --- src/database/models/UserAgents.ts | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/database/models/UserAgents.ts b/src/database/models/UserAgents.ts index e411086..f5950c2 100644 --- a/src/database/models/UserAgents.ts +++ b/src/database/models/UserAgents.ts @@ -21,4 +21,44 @@ export const userAgentsModel = new Model( } ); +// Original type for ORM operations export type UserAgentsTable = Infer; + +// CamelCase interface for application use +export interface UserAgent { + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + crewId: number; + agentName: string; + agentRole: string; + agentGoal: string; + agentBackstory: string; + agentTools?: string; +} + +// Transform from snake_case to camelCase +export const toCamelCase = (agent: UserAgentsTable): UserAgent => ({ + id: agent.id, + createdAt: agent.created_at, + updatedAt: agent.updated_at, + profileId: agent.profile_id, + crewId: agent.crew_id, + agentName: agent.agent_name, + agentRole: agent.agent_role, + agentGoal: agent.agent_goal, + agentBackstory: agent.agent_backstory, + agentTools: agent.agent_tools, +}); + +// Transform from camelCase to snake_case +export const toSnakeCase = (agent: Partial): Partial> => ({ + ...(agent.profileId && { profile_id: agent.profileId }), + ...(agent.crewId && { crew_id: agent.crewId }), + ...(agent.agentName && { agent_name: agent.agentName }), + ...(agent.agentRole && { agent_role: agent.agentRole }), + ...(agent.agentGoal && { agent_goal: agent.agentGoal }), + ...(agent.agentBackstory && { agent_backstory: agent.agentBackstory }), + ...(agent.agentTools && { agent_tools: agent.agentTools }), +}); From 03f1874a3720874b76fc38606ef6801bd5bae83f Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:28:57 -0700 Subject: [PATCH 102/128] refactor: Generalize case transformation utilities with generic types --- src/database/models/UserAgents.ts | 28 +++++------------------ src/utils/case-transformers.ts | 37 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 src/utils/case-transformers.ts diff --git a/src/database/models/UserAgents.ts b/src/database/models/UserAgents.ts index f5950c2..f51c4b5 100644 --- a/src/database/models/UserAgents.ts +++ b/src/database/models/UserAgents.ts @@ -38,27 +38,9 @@ export interface UserAgent { agentTools?: string; } -// Transform from snake_case to camelCase -export const toCamelCase = (agent: UserAgentsTable): UserAgent => ({ - id: agent.id, - createdAt: agent.created_at, - updatedAt: agent.updated_at, - profileId: agent.profile_id, - crewId: agent.crew_id, - agentName: agent.agent_name, - agentRole: agent.agent_role, - agentGoal: agent.agent_goal, - agentBackstory: agent.agent_backstory, - agentTools: agent.agent_tools, -}); +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; -// Transform from camelCase to snake_case -export const toSnakeCase = (agent: Partial): Partial> => ({ - ...(agent.profileId && { profile_id: agent.profileId }), - ...(agent.crewId && { crew_id: agent.crewId }), - ...(agent.agentName && { agent_name: agent.agentName }), - ...(agent.agentRole && { agent_role: agent.agentRole }), - ...(agent.agentGoal && { agent_goal: agent.agentGoal }), - ...(agent.agentBackstory && { agent_backstory: agent.agentBackstory }), - ...(agent.agentTools && { agent_tools: agent.agentTools }), -}); +// Transform functions using generic utility +export const transformToCamelCase = (agent: UserAgentsTable): UserAgent => toCamelCase(agent); +export const transformToSnakeCase = (agent: Partial): Partial> => + toSnakeCase(agent); diff --git a/src/utils/case-transformers.ts b/src/utils/case-transformers.ts new file mode 100644 index 0000000..88d826c --- /dev/null +++ b/src/utils/case-transformers.ts @@ -0,0 +1,37 @@ +type CamelToSnakeCase = S extends `${infer T}${infer U}` + ? `${T extends Capitalize ? '_' : ''}${Lowercase}${CamelToSnakeCase}` + : S; + +type SnakeToCamelCase = S extends `${infer T}_${infer U}` + ? `${T}${Capitalize>}` + : S; + +type CamelToSnake = { + [K in keyof T as CamelToSnakeCase]: T[K]; +}; + +type SnakeToCamel = { + [K in keyof T as SnakeToCamelCase]: T[K]; +}; + +export const toCamelCase = >(obj: T): SnakeToCamel => { + const camelObj: any = {}; + Object.keys(obj).forEach((key) => { + if (obj[key] !== undefined) { + const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); + camelObj[camelKey] = obj[key]; + } + }); + return camelObj; +}; + +export const toSnakeCase = >(obj: T): CamelToSnake => { + const snakeObj: any = {}; + Object.keys(obj).forEach((key) => { + if (obj[key] !== undefined) { + const snakeKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`); + snakeObj[snakeKey] = obj[key]; + } + }); + return snakeObj; +}; From e74357a4254b066ba6abfebedaa8976d0e77905f Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:29:57 -0700 Subject: [PATCH 103/128] feat: Update UserCrons model with camelCase interface and case transformers --- src/database/models/UserCrons.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/database/models/UserCrons.ts b/src/database/models/UserCrons.ts index 4800be5..85b288b 100644 --- a/src/database/models/UserCrons.ts +++ b/src/database/models/UserCrons.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userCronsModel = new Model( { @@ -19,4 +20,22 @@ export const userCronsModel = new Model( } ); +// Original type for ORM operations export type UserCronsTable = Infer; + +// CamelCase interface for application use +export interface UserCron { + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + crewId: number; + cronEnabled: boolean; + cronInterval: string; + cronInput: string; +} + +// Transform functions using generic utility +export const transformToCamelCase = (cron: UserCronsTable): UserCron => toCamelCase(cron); +export const transformToSnakeCase = (cron: Partial): Partial> => + toSnakeCase(cron); From 41376e296f9bfcdef02b83069e92e42ad7e85ed6 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:30:17 -0700 Subject: [PATCH 104/128] feat: Add camelCase interface and case transformation for UserCrews model --- src/database/models/UserCrews.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/database/models/UserCrews.ts b/src/database/models/UserCrews.ts index d39a1a5..a6e9666 100644 --- a/src/database/models/UserCrews.ts +++ b/src/database/models/UserCrews.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userCrewsModel = new Model( { @@ -20,4 +21,23 @@ export const userCrewsModel = new Model( } ); +// Original type for ORM operations export type UserCrewsTable = Infer; + +// CamelCase interface for application use +export interface UserCrew { + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + crewName: string; + crewDescription?: string; + crewExecutions?: number; + crewIsPublic?: boolean; + crewIsCron?: boolean; +} + +// Transform functions using generic utility +export const transformToCamelCase = (crew: UserCrewsTable): UserCrew => toCamelCase(crew); +export const transformToSnakeCase = (crew: Partial): Partial> => + toSnakeCase(crew); From cb66a86bf9252a2b23d4a12af38dfdfcd93a9bf8 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:30:37 -0700 Subject: [PATCH 105/128] feat: Add case transformation and interface for UserTasks model --- src/database/models/UserTasks.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/database/models/UserTasks.ts b/src/database/models/UserTasks.ts index 257c40b..7739b43 100644 --- a/src/database/models/UserTasks.ts +++ b/src/database/models/UserTasks.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userTasksModel = new Model( { @@ -20,4 +21,23 @@ export const userTasksModel = new Model( } ); +// Original type for ORM operations export type UserTasksTable = Infer; + +// CamelCase interface for application use +export interface UserTask { + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + crewId: number; + agentId: number; + taskName: string; + taskDescription: string; + taskExpectedOutput: string; +} + +// Transform functions using generic utility +export const transformToCamelCase = (task: UserTasksTable): UserTask => toCamelCase(task); +export const transformToSnakeCase = (task: Partial): Partial> => + toSnakeCase(task); From f699664350fc629cc2efd2a938a2b696c88daf85 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:30:54 -0700 Subject: [PATCH 106/128] feat: Add case transformation utilities to UserCrewExecutions model --- src/database/models/UserCrewExecutions.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/database/models/UserCrewExecutions.ts b/src/database/models/UserCrewExecutions.ts index 68425c6..ce47c3c 100644 --- a/src/database/models/UserCrewExecutions.ts +++ b/src/database/models/UserCrewExecutions.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userCrewExecutionsModel = new Model( { @@ -21,4 +22,24 @@ export const userCrewExecutionsModel = new Model( } ); +// Original type for ORM operations export type UserCrewExecutionsTable = Infer; + +// CamelCase interface for application use +export interface UserCrewExecution { + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + crewId: number; + conversationId: number; + userInput?: string; + finalResult?: string; + totalTokens?: number; + successfulRequests?: number; +} + +// Transform functions using generic utility +export const transformToCamelCase = (execution: UserCrewExecutionsTable): UserCrewExecution => toCamelCase(execution); +export const transformToSnakeCase = (execution: Partial): Partial> => + toSnakeCase(execution); From 05f06234bca7c2f8c79aa010c7f55c532a392e21 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:33:46 -0700 Subject: [PATCH 107/128] feat: Add case transformation utilities to UserCrewExecutionSteps model --- src/database/models/UserCrewExecutionSteps.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/database/models/UserCrewExecutionSteps.ts b/src/database/models/UserCrewExecutionSteps.ts index 33567e4..48e0ff3 100644 --- a/src/database/models/UserCrewExecutionSteps.ts +++ b/src/database/models/UserCrewExecutionSteps.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userCrewExecutionStepsModel = new Model( { @@ -18,4 +19,21 @@ export const userCrewExecutionStepsModel = new Model( } ); +// Original type for ORM operations export type UserCrewExecutionStepsTable = Infer; + +// CamelCase interface for application use +export interface UserCrewExecutionStep { + id: number; + createdAt: string; + profileId: string; + crewId: number; + executionId: number; + stepType: string; + stepData: string; +} + +// Transform functions using generic utility +export const transformToCamelCase = (step: UserCrewExecutionStepsTable): UserCrewExecutionStep => toCamelCase(step); +export const transformToSnakeCase = (step: Partial): Partial> => + toSnakeCase(step); From b0cbb5ab5c0cb01eabb691178b9e32353b00a514 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:34:01 -0700 Subject: [PATCH 108/128] feat: Add case transformation utilities to UserConversations model --- src/database/models/UserConversations.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/database/models/UserConversations.ts b/src/database/models/UserConversations.ts index 0b480cb..02f5f93 100644 --- a/src/database/models/UserConversations.ts +++ b/src/database/models/UserConversations.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userConversationsModel = new Model( { @@ -16,4 +17,19 @@ export const userConversationsModel = new Model( } ); +// Original type for ORM operations export type UserConversationsTable = Infer; + +// CamelCase interface for application use +export interface UserConversation { + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + conversationName: string; +} + +// Transform functions using generic utility +export const transformToCamelCase = (conversation: UserConversationsTable): UserConversation => toCamelCase(conversation); +export const transformToSnakeCase = (conversation: Partial): Partial> => + toSnakeCase(conversation); From 6bd0e13b2f902f20227fc63b965ef1f116fb60db Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:34:13 -0700 Subject: [PATCH 109/128] feat: Add case transformation and interface for UserProfile model --- src/database/models/UserProfile.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/database/models/UserProfile.ts b/src/database/models/UserProfile.ts index baca129..5722137 100644 --- a/src/database/models/UserProfile.ts +++ b/src/database/models/UserProfile.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userProfilesModel = new Model( { @@ -19,4 +20,21 @@ export const userProfilesModel = new Model( } ); +// Original type for ORM operations export type UserProfilesTable = Infer; + +// CamelCase interface for application use +export interface UserProfile { + id: number; + createdAt: string; + updatedAt: string; + userRole: string; + accountIndex?: number; + stxAddress: string; + bnsAddress?: string; +} + +// Transform functions using generic utility +export const transformToCamelCase = (profile: UserProfilesTable): UserProfile => toCamelCase(profile); +export const transformToSnakeCase = (profile: Partial): Partial> => + toSnakeCase(profile); From 61900e296d3548d76af58a540d7f970ea1e705f1 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:34:29 -0700 Subject: [PATCH 110/128] feat: Add case transformation utilities to UserSocials model --- src/database/models/UserSocials.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/database/models/UserSocials.ts b/src/database/models/UserSocials.ts index eb9c617..60c335a 100644 --- a/src/database/models/UserSocials.ts +++ b/src/database/models/UserSocials.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userSocialsModel = new Model( { @@ -17,4 +18,20 @@ export const userSocialsModel = new Model( } ); +// Original type for ORM operations export type UserSocialsTable = Infer; + +// CamelCase interface for application use +export interface UserSocial { + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + platform: string; + platformId: string; +} + +// Transform functions using generic utility +export const transformToCamelCase = (social: UserSocialsTable): UserSocial => toCamelCase(social); +export const transformToSnakeCase = (social: Partial): Partial> => + toSnakeCase(social); From 436a4d7441382457890a7b1f2ea69e8f4dd0d51a Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:34:41 -0700 Subject: [PATCH 111/128] feat: Add case transformation utilities to XBotAuthors model --- src/database/models/XBotAuthors.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/database/models/XBotAuthors.ts b/src/database/models/XBotAuthors.ts index 74a193f..8430edb 100644 --- a/src/database/models/XBotAuthors.ts +++ b/src/database/models/XBotAuthors.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const xBotAuthorsModel = new Model( { @@ -17,4 +18,20 @@ export const xBotAuthorsModel = new Model( } ); +// Original type for ORM operations export type XBotAuthorsTable = Infer; + +// CamelCase interface for application use +export interface XBotAuthor { + id: number; + createdAt: string; + updatedAt: string; + authorId: string; + realname?: string; + username?: string; +} + +// Transform functions using generic utility +export const transformToCamelCase = (author: XBotAuthorsTable): XBotAuthor => toCamelCase(author); +export const transformToSnakeCase = (author: Partial): Partial> => + toSnakeCase(author); From 6b39b52cf6e58f01a743dbdfdd9ebb61e502c4c7 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:34:54 -0700 Subject: [PATCH 112/128] feat: Add case transformation utilities to XBotThreads model --- src/database/models/XBotThreads.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/database/models/XBotThreads.ts b/src/database/models/XBotThreads.ts index cb0412c..0561584 100644 --- a/src/database/models/XBotThreads.ts +++ b/src/database/models/XBotThreads.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const xBotThreadsModel = new Model( { @@ -13,4 +14,16 @@ export const xBotThreadsModel = new Model( } ); +// Original type for ORM operations export type XBotThreadsTable = Infer; + +// CamelCase interface for application use +export interface XBotThread { + id: number; + createdAt: string; +} + +// Transform functions using generic utility +export const transformToCamelCase = (thread: XBotThreadsTable): XBotThread => toCamelCase(thread); +export const transformToSnakeCase = (thread: Partial): Partial> => + toSnakeCase(thread); From c1dc347bb01a5f2a274c092d48137965b0da2fc8 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:35:30 -0700 Subject: [PATCH 113/128] feat: Add case transformation utilities to XBotTweets model --- src/database/models/XBotTweets.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/database/models/XBotTweets.ts b/src/database/models/XBotTweets.ts index 0aa9549..6cd1612 100644 --- a/src/database/models/XBotTweets.ts +++ b/src/database/models/XBotTweets.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const xBotTweetsModel = new Model( { @@ -22,4 +23,25 @@ export const xBotTweetsModel = new Model( } ); +// Original type for ORM operations export type XBotTweetsTable = Infer; + +// CamelCase interface for application use +export interface XBotTweet { + id: number; + createdAt: string; + updatedAt: string; + authorId: string; + threadId?: number; + parentTweetId?: string; + tweetId: string; + tweetCreatedAt?: string; + tweetUpdatedAt?: string; + tweetBody?: string; + isBotResponse?: boolean; +} + +// Transform functions using generic utility +export const transformToCamelCase = (tweet: XBotTweetsTable): XBotTweet => toCamelCase(tweet); +export const transformToSnakeCase = (tweet: Partial): Partial> => + toSnakeCase(tweet); From a5a91efd60b9564f869feeec61af0e182e537d20 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:35:57 -0700 Subject: [PATCH 114/128] refactor: Update XBotLogs model with case transformation utilities --- src/database/models/XBotLogs.ts | 16 ++++++++++++++++ src/database/models/index.ts | 4 ---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/database/models/XBotLogs.ts b/src/database/models/XBotLogs.ts index 35bd976..d962242 100644 --- a/src/database/models/XBotLogs.ts +++ b/src/database/models/XBotLogs.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const xBotLogsModel = new Model( { @@ -16,4 +17,19 @@ export const xBotLogsModel = new Model( } ); +// Original type for ORM operations export type XBotLogsTable = Infer; + +// CamelCase interface for application use +export interface XBotLog { + id: number; + createdAt: string; + tweetId: string; + tweetStatus?: string; + logMessage?: string; +} + +// Transform functions using generic utility +export const transformToCamelCase = (log: XBotLogsTable): XBotLog => toCamelCase(log); +export const transformToSnakeCase = (log: Partial): Partial> => + toSnakeCase(log); diff --git a/src/database/models/index.ts b/src/database/models/index.ts index 85a4c15..127136b 100644 --- a/src/database/models/index.ts +++ b/src/database/models/index.ts @@ -11,7 +11,3 @@ export * from './XBotAuthors'; export * from './XBotThreads'; export * from './XBotTweets'; export * from './XBotLogs'; -export * from './XBotAuthors'; -export * from './XBotThreads'; -export * from './XBotTweets'; -export * from './XBotLogs'; From 7ecb0c4a321a0e6ccf3e1d9afd8ef68b98b2eb4f Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:36:56 -0700 Subject: [PATCH 115/128] refactor: Standardize import order, comment spacing, and file structure in UserAgents.ts --- src/database/models/UserAgents.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/database/models/UserAgents.ts b/src/database/models/UserAgents.ts index f51c4b5..31295bb 100644 --- a/src/database/models/UserAgents.ts +++ b/src/database/models/UserAgents.ts @@ -1,4 +1,5 @@ import { Model, DataTypes, Infer } from 'd1-orm'; +import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userAgentsModel = new Model( { @@ -38,8 +39,6 @@ export interface UserAgent { agentTools?: string; } -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; - // Transform functions using generic utility export const transformToCamelCase = (agent: UserAgentsTable): UserAgent => toCamelCase(agent); export const transformToSnakeCase = (agent: Partial): Partial> => From fc30389bfad1b79927d50c52865f510d49dc0a69 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:38:07 -0700 Subject: [PATCH 116/128] style: Standardize model interfaces and formatting across files --- src/database/models/UserCrewExecutionSteps.ts | 15 ++++++++------- src/database/models/XBotThreads.ts | 4 +++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/database/models/UserCrewExecutionSteps.ts b/src/database/models/UserCrewExecutionSteps.ts index 48e0ff3..77b720b 100644 --- a/src/database/models/UserCrewExecutionSteps.ts +++ b/src/database/models/UserCrewExecutionSteps.ts @@ -24,13 +24,14 @@ export type UserCrewExecutionStepsTable = Infer; export interface XBotThread { id: number; createdAt: string; + updatedAt: string; } // Transform functions using generic utility export const transformToCamelCase = (thread: XBotThreadsTable): XBotThread => toCamelCase(thread); -export const transformToSnakeCase = (thread: Partial): Partial> => +export const transformToSnakeCase = (thread: Partial): Partial> => toSnakeCase(thread); From 6cfe32d77e3250724843ce4d92c528b9d4945219 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:38:27 -0700 Subject: [PATCH 117/128] refactor: Add updated_at field to UserCrewExecutionSteps and XBotLogs models --- src/database/models/UserCrewExecutionSteps.ts | 3 ++- src/database/models/XBotLogs.ts | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/database/models/UserCrewExecutionSteps.ts b/src/database/models/UserCrewExecutionSteps.ts index 77b720b..45980af 100644 --- a/src/database/models/UserCrewExecutionSteps.ts +++ b/src/database/models/UserCrewExecutionSteps.ts @@ -11,6 +11,7 @@ export const userCrewExecutionStepsModel = new Model( { id: { type: DataTypes.INTEGER, notNull: true }, created_at: { type: DataTypes.STRING }, + updated_at: { type: DataTypes.STRING }, profile_id: { type: DataTypes.STRING, notNull: true }, crew_id: { type: DataTypes.INTEGER, notNull: true }, execution_id: { type: DataTypes.INTEGER, notNull: true }, @@ -36,5 +37,5 @@ export interface UserCrewExecutionStep { // Transform functions using generic utility export const transformToCamelCase = (step: UserCrewExecutionStepsTable): UserCrewExecutionStep => toCamelCase(step); -export const transformToSnakeCase = (step: Partial): Partial> => +export const transformToSnakeCase = (step: Partial): Partial> => toSnakeCase(step); diff --git a/src/database/models/XBotLogs.ts b/src/database/models/XBotLogs.ts index d962242..4e32668 100644 --- a/src/database/models/XBotLogs.ts +++ b/src/database/models/XBotLogs.ts @@ -11,6 +11,7 @@ export const xBotLogsModel = new Model( { id: { type: DataTypes.INTEGER, notNull: true }, created_at: { type: DataTypes.STRING }, + updated_at: { type: DataTypes.STRING }, tweet_id: { type: DataTypes.STRING, notNull: true }, tweet_status: { type: DataTypes.STRING }, log_message: { type: DataTypes.STRING }, @@ -24,6 +25,7 @@ export type XBotLogsTable = Infer; export interface XBotLog { id: number; createdAt: string; + updatedAt: string; tweetId: string; tweetStatus?: string; logMessage?: string; @@ -31,5 +33,5 @@ export interface XBotLog { // Transform functions using generic utility export const transformToCamelCase = (log: XBotLogsTable): XBotLog => toCamelCase(log); -export const transformToSnakeCase = (log: Partial): Partial> => +export const transformToSnakeCase = (log: Partial): Partial> => toSnakeCase(log); From f6eb5ba2a7e0060cae29ec3e9c392bce488762e1 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:42:09 -0700 Subject: [PATCH 118/128] refactor: Rename transform functions with model-specific prefixes --- src/database/models/UserConversations.ts | 4 ++-- src/database/models/UserCrewExecutionSteps.ts | 4 ++-- src/database/models/UserCrewExecutions.ts | 4 ++-- src/database/models/UserCrews.ts | 4 ++-- src/database/models/UserCrons.ts | 4 ++-- src/database/models/UserProfile.ts | 4 ++-- src/database/models/UserSocials.ts | 4 ++-- src/database/models/UserTasks.ts | 4 ++-- src/database/models/XBotAuthors.ts | 4 ++-- src/database/models/XBotLogs.ts | 4 ++-- src/database/models/XBotThreads.ts | 4 ++-- src/database/models/XBotTweets.ts | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/database/models/UserConversations.ts b/src/database/models/UserConversations.ts index 02f5f93..e5d3efc 100644 --- a/src/database/models/UserConversations.ts +++ b/src/database/models/UserConversations.ts @@ -30,6 +30,6 @@ export interface UserConversation { } // Transform functions using generic utility -export const transformToCamelCase = (conversation: UserConversationsTable): UserConversation => toCamelCase(conversation); -export const transformToSnakeCase = (conversation: Partial): Partial> => +export const transformUserConversationToCamelCase = (conversation: UserConversationsTable): UserConversation => toCamelCase(conversation); +export const transformUserConversationToSnakeCase = (conversation: Partial): Partial> => toSnakeCase(conversation); diff --git a/src/database/models/UserCrewExecutionSteps.ts b/src/database/models/UserCrewExecutionSteps.ts index 45980af..06ee501 100644 --- a/src/database/models/UserCrewExecutionSteps.ts +++ b/src/database/models/UserCrewExecutionSteps.ts @@ -36,6 +36,6 @@ export interface UserCrewExecutionStep { } // Transform functions using generic utility -export const transformToCamelCase = (step: UserCrewExecutionStepsTable): UserCrewExecutionStep => toCamelCase(step); -export const transformToSnakeCase = (step: Partial): Partial> => +export const transformUserCrewExecutionStepToCamelCase = (step: UserCrewExecutionStepsTable): UserCrewExecutionStep => toCamelCase(step); +export const transformUserCrewExecutionStepToSnakeCase = (step: Partial): Partial> => toSnakeCase(step); diff --git a/src/database/models/UserCrewExecutions.ts b/src/database/models/UserCrewExecutions.ts index ce47c3c..12eb25e 100644 --- a/src/database/models/UserCrewExecutions.ts +++ b/src/database/models/UserCrewExecutions.ts @@ -40,6 +40,6 @@ export interface UserCrewExecution { } // Transform functions using generic utility -export const transformToCamelCase = (execution: UserCrewExecutionsTable): UserCrewExecution => toCamelCase(execution); -export const transformToSnakeCase = (execution: Partial): Partial> => +export const transformUserCrewExecutionToCamelCase = (execution: UserCrewExecutionsTable): UserCrewExecution => toCamelCase(execution); +export const transformUserCrewExecutionToSnakeCase = (execution: Partial): Partial> => toSnakeCase(execution); diff --git a/src/database/models/UserCrews.ts b/src/database/models/UserCrews.ts index a6e9666..69a2603 100644 --- a/src/database/models/UserCrews.ts +++ b/src/database/models/UserCrews.ts @@ -38,6 +38,6 @@ export interface UserCrew { } // Transform functions using generic utility -export const transformToCamelCase = (crew: UserCrewsTable): UserCrew => toCamelCase(crew); -export const transformToSnakeCase = (crew: Partial): Partial> => +export const transformUserCrewToCamelCase = (crew: UserCrewsTable): UserCrew => toCamelCase(crew); +export const transformUserCrewToSnakeCase = (crew: Partial): Partial> => toSnakeCase(crew); diff --git a/src/database/models/UserCrons.ts b/src/database/models/UserCrons.ts index 85b288b..8a44605 100644 --- a/src/database/models/UserCrons.ts +++ b/src/database/models/UserCrons.ts @@ -36,6 +36,6 @@ export interface UserCron { } // Transform functions using generic utility -export const transformToCamelCase = (cron: UserCronsTable): UserCron => toCamelCase(cron); -export const transformToSnakeCase = (cron: Partial): Partial> => +export const transformUserCronToCamelCase = (cron: UserCronsTable): UserCron => toCamelCase(cron); +export const transformUserCronToSnakeCase = (cron: Partial): Partial> => toSnakeCase(cron); diff --git a/src/database/models/UserProfile.ts b/src/database/models/UserProfile.ts index 5722137..99e89b9 100644 --- a/src/database/models/UserProfile.ts +++ b/src/database/models/UserProfile.ts @@ -35,6 +35,6 @@ export interface UserProfile { } // Transform functions using generic utility -export const transformToCamelCase = (profile: UserProfilesTable): UserProfile => toCamelCase(profile); -export const transformToSnakeCase = (profile: Partial): Partial> => +export const transformUserProfileToCamelCase = (profile: UserProfilesTable): UserProfile => toCamelCase(profile); +export const transformUserProfileToSnakeCase = (profile: Partial): Partial> => toSnakeCase(profile); diff --git a/src/database/models/UserSocials.ts b/src/database/models/UserSocials.ts index 60c335a..3435e55 100644 --- a/src/database/models/UserSocials.ts +++ b/src/database/models/UserSocials.ts @@ -32,6 +32,6 @@ export interface UserSocial { } // Transform functions using generic utility -export const transformToCamelCase = (social: UserSocialsTable): UserSocial => toCamelCase(social); -export const transformToSnakeCase = (social: Partial): Partial> => +export const transformUserSocialToCamelCase = (social: UserSocialsTable): UserSocial => toCamelCase(social); +export const transformUserSocialToSnakeCase = (social: Partial): Partial> => toSnakeCase(social); diff --git a/src/database/models/UserTasks.ts b/src/database/models/UserTasks.ts index 7739b43..dddeac9 100644 --- a/src/database/models/UserTasks.ts +++ b/src/database/models/UserTasks.ts @@ -38,6 +38,6 @@ export interface UserTask { } // Transform functions using generic utility -export const transformToCamelCase = (task: UserTasksTable): UserTask => toCamelCase(task); -export const transformToSnakeCase = (task: Partial): Partial> => +export const transformUserTaskToCamelCase = (task: UserTasksTable): UserTask => toCamelCase(task); +export const transformUserTaskToSnakeCase = (task: Partial): Partial> => toSnakeCase(task); diff --git a/src/database/models/XBotAuthors.ts b/src/database/models/XBotAuthors.ts index 8430edb..998550d 100644 --- a/src/database/models/XBotAuthors.ts +++ b/src/database/models/XBotAuthors.ts @@ -32,6 +32,6 @@ export interface XBotAuthor { } // Transform functions using generic utility -export const transformToCamelCase = (author: XBotAuthorsTable): XBotAuthor => toCamelCase(author); -export const transformToSnakeCase = (author: Partial): Partial> => +export const transformXBotAuthorToCamelCase = (author: XBotAuthorsTable): XBotAuthor => toCamelCase(author); +export const transformXBotAuthorToSnakeCase = (author: Partial): Partial> => toSnakeCase(author); diff --git a/src/database/models/XBotLogs.ts b/src/database/models/XBotLogs.ts index 4e32668..ade6683 100644 --- a/src/database/models/XBotLogs.ts +++ b/src/database/models/XBotLogs.ts @@ -32,6 +32,6 @@ export interface XBotLog { } // Transform functions using generic utility -export const transformToCamelCase = (log: XBotLogsTable): XBotLog => toCamelCase(log); -export const transformToSnakeCase = (log: Partial): Partial> => +export const transformXBotLogToCamelCase = (log: XBotLogsTable): XBotLog => toCamelCase(log); +export const transformXBotLogToSnakeCase = (log: Partial): Partial> => toSnakeCase(log); diff --git a/src/database/models/XBotThreads.ts b/src/database/models/XBotThreads.ts index d113e62..ca6c532 100644 --- a/src/database/models/XBotThreads.ts +++ b/src/database/models/XBotThreads.ts @@ -26,6 +26,6 @@ export interface XBotThread { } // Transform functions using generic utility -export const transformToCamelCase = (thread: XBotThreadsTable): XBotThread => toCamelCase(thread); -export const transformToSnakeCase = (thread: Partial): Partial> => +export const transformXBotThreadToCamelCase = (thread: XBotThreadsTable): XBotThread => toCamelCase(thread); +export const transformXBotThreadToSnakeCase = (thread: Partial): Partial> => toSnakeCase(thread); diff --git a/src/database/models/XBotTweets.ts b/src/database/models/XBotTweets.ts index 6cd1612..5c57333 100644 --- a/src/database/models/XBotTweets.ts +++ b/src/database/models/XBotTweets.ts @@ -42,6 +42,6 @@ export interface XBotTweet { } // Transform functions using generic utility -export const transformToCamelCase = (tweet: XBotTweetsTable): XBotTweet => toCamelCase(tweet); -export const transformToSnakeCase = (tweet: Partial): Partial> => +export const transformXBotTweetToCamelCase = (tweet: XBotTweetsTable): XBotTweet => toCamelCase(tweet); +export const transformXBotTweetToSnakeCase = (tweet: Partial): Partial> => toSnakeCase(tweet); From ab5dfa3924ace836a9cfde0babbefcab9121b89b Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Mon, 23 Dec 2024 15:42:41 -0700 Subject: [PATCH 119/128] refactor: Update UserAgents model with consistent indentation --- src/database/models/UserAgents.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/database/models/UserAgents.ts b/src/database/models/UserAgents.ts index 31295bb..34d982e 100644 --- a/src/database/models/UserAgents.ts +++ b/src/database/models/UserAgents.ts @@ -27,19 +27,19 @@ export type UserAgentsTable = Infer; // CamelCase interface for application use export interface UserAgent { - id: number; - createdAt: string; - updatedAt: string; - profileId: string; - crewId: number; - agentName: string; - agentRole: string; - agentGoal: string; - agentBackstory: string; - agentTools?: string; + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + crewId: number; + agentName: string; + agentRole: string; + agentGoal: string; + agentBackstory: string; + agentTools?: string; } // Transform functions using generic utility export const transformToCamelCase = (agent: UserAgentsTable): UserAgent => toCamelCase(agent); -export const transformToSnakeCase = (agent: Partial): Partial> => - toSnakeCase(agent); +export const transformToSnakeCase = (agent: Partial): Partial> => + toSnakeCase(agent); From 38f3e201ea913288997223d5dbd8d1fcbbe7b18e Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:42:43 -0700 Subject: [PATCH 120/128] refactor: Improve UserAgent transform functions with explicit type mapping --- src/database/models/UserAgents.ts | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/database/models/UserAgents.ts b/src/database/models/UserAgents.ts index 34d982e..a6b72fd 100644 --- a/src/database/models/UserAgents.ts +++ b/src/database/models/UserAgents.ts @@ -40,6 +40,25 @@ export interface UserAgent { } // Transform functions using generic utility -export const transformToCamelCase = (agent: UserAgentsTable): UserAgent => toCamelCase(agent); -export const transformToSnakeCase = (agent: Partial): Partial> => - toSnakeCase(agent); +export const transformUserAgentToCamelCase = (agent: UserAgentsTable): UserAgent => ({ + id: agent.id, + createdAt: agent.created_at || '', + updatedAt: agent.updated_at || '', + profileId: agent.profile_id, + crewId: agent.crew_id, + agentName: agent.agent_name, + agentRole: agent.agent_role, + agentGoal: agent.agent_goal, + agentBackstory: agent.agent_backstory, + agentTools: agent.agent_tools, +}); + +export const transformUserAgentToSnakeCase = (agent: Partial): Partial> => ({ + profile_id: agent.profileId, + crew_id: agent.crewId, + agent_name: agent.agentName, + agent_role: agent.agentRole, + agent_goal: agent.agentGoal, + agent_backstory: agent.agentBackstory, + agent_tools: agent.agentTools, +}); From cd3261a1ec5baaa121f1f9068ff2e381396e9ab8 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:44:03 -0700 Subject: [PATCH 121/128] refactor: Replace generic transform functions with explicit mapping for all models --- src/database/models/UserConversations.ts | 17 ++++++++--- src/database/models/UserCrewExecutionSteps.ts | 23 ++++++++++++--- src/database/models/UserCrews.ts | 25 +++++++++++++--- src/database/models/UserCrons.ts | 23 ++++++++++++--- src/database/models/UserProfile.ts | 21 +++++++++++--- src/database/models/UserTasks.ts | 25 +++++++++++++--- src/database/models/XBotAuthors.ts | 19 +++++++++--- src/database/models/XBotLogs.ts | 19 +++++++++--- src/database/models/XBotThreads.ts | 12 +++++--- src/database/models/XBotTweets.ts | 29 ++++++++++++++++--- 10 files changed, 173 insertions(+), 40 deletions(-) diff --git a/src/database/models/UserConversations.ts b/src/database/models/UserConversations.ts index e5d3efc..c3757e1 100644 --- a/src/database/models/UserConversations.ts +++ b/src/database/models/UserConversations.ts @@ -29,7 +29,16 @@ export interface UserConversation { conversationName: string; } -// Transform functions using generic utility -export const transformUserConversationToCamelCase = (conversation: UserConversationsTable): UserConversation => toCamelCase(conversation); -export const transformUserConversationToSnakeCase = (conversation: Partial): Partial> => - toSnakeCase(conversation); +// Transform functions with explicit mapping +export const transformUserConversationToCamelCase = (conversation: UserConversationsTable): UserConversation => ({ + id: conversation.id, + createdAt: conversation.created_at || '', + updatedAt: conversation.updated_at || '', + profileId: conversation.profile_id, + conversationName: conversation.conversation_name +}); + +export const transformUserConversationToSnakeCase = (conversation: Partial): Partial> => ({ + profile_id: conversation.profileId, + conversation_name: conversation.conversationName +}); diff --git a/src/database/models/UserCrewExecutionSteps.ts b/src/database/models/UserCrewExecutionSteps.ts index 06ee501..0c75106 100644 --- a/src/database/models/UserCrewExecutionSteps.ts +++ b/src/database/models/UserCrewExecutionSteps.ts @@ -35,7 +35,22 @@ export interface UserCrewExecutionStep { stepData: string; } -// Transform functions using generic utility -export const transformUserCrewExecutionStepToCamelCase = (step: UserCrewExecutionStepsTable): UserCrewExecutionStep => toCamelCase(step); -export const transformUserCrewExecutionStepToSnakeCase = (step: Partial): Partial> => - toSnakeCase(step); +// Transform functions with explicit mapping +export const transformUserCrewExecutionStepToCamelCase = (step: UserCrewExecutionStepsTable): UserCrewExecutionStep => ({ + id: step.id, + createdAt: step.created_at || '', + updatedAt: step.updated_at || '', + profileId: step.profile_id, + crewId: step.crew_id, + executionId: step.execution_id, + stepType: step.step_type, + stepData: step.step_data +}); + +export const transformUserCrewExecutionStepToSnakeCase = (step: Partial): Partial> => ({ + profile_id: step.profileId, + crew_id: step.crewId, + execution_id: step.executionId, + step_type: step.stepType, + step_data: step.stepData +}); diff --git a/src/database/models/UserCrews.ts b/src/database/models/UserCrews.ts index 69a2603..90fd41f 100644 --- a/src/database/models/UserCrews.ts +++ b/src/database/models/UserCrews.ts @@ -37,7 +37,24 @@ export interface UserCrew { crewIsCron?: boolean; } -// Transform functions using generic utility -export const transformUserCrewToCamelCase = (crew: UserCrewsTable): UserCrew => toCamelCase(crew); -export const transformUserCrewToSnakeCase = (crew: Partial): Partial> => - toSnakeCase(crew); +// Transform functions with explicit mapping +export const transformUserCrewToCamelCase = (crew: UserCrewsTable): UserCrew => ({ + id: crew.id, + createdAt: crew.created_at || '', + updatedAt: crew.updated_at || '', + profileId: crew.profile_id, + crewName: crew.crew_name, + crewDescription: crew.crew_description, + crewExecutions: crew.crew_executions, + crewIsPublic: crew.crew_is_public, + crewIsCron: crew.crew_is_cron +}); + +export const transformUserCrewToSnakeCase = (crew: Partial): Partial> => ({ + profile_id: crew.profileId, + crew_name: crew.crewName, + crew_description: crew.crewDescription, + crew_executions: crew.crewExecutions, + crew_is_public: crew.crewIsPublic, + crew_is_cron: crew.crewIsCron +}); diff --git a/src/database/models/UserCrons.ts b/src/database/models/UserCrons.ts index 8a44605..69641df 100644 --- a/src/database/models/UserCrons.ts +++ b/src/database/models/UserCrons.ts @@ -35,7 +35,22 @@ export interface UserCron { cronInput: string; } -// Transform functions using generic utility -export const transformUserCronToCamelCase = (cron: UserCronsTable): UserCron => toCamelCase(cron); -export const transformUserCronToSnakeCase = (cron: Partial): Partial> => - toSnakeCase(cron); +// Transform functions with explicit mapping +export const transformUserCronToCamelCase = (cron: UserCronsTable): UserCron => ({ + id: cron.id, + createdAt: cron.created_at || '', + updatedAt: cron.updated_at || '', + profileId: cron.profile_id, + crewId: cron.crew_id, + cronEnabled: cron.cron_enabled, + cronInterval: cron.cron_interval, + cronInput: cron.cron_input +}); + +export const transformUserCronToSnakeCase = (cron: Partial): Partial> => ({ + profile_id: cron.profileId, + crew_id: cron.crewId, + cron_enabled: cron.cronEnabled, + cron_interval: cron.cronInterval, + cron_input: cron.cronInput +}); diff --git a/src/database/models/UserProfile.ts b/src/database/models/UserProfile.ts index 99e89b9..2949839 100644 --- a/src/database/models/UserProfile.ts +++ b/src/database/models/UserProfile.ts @@ -34,7 +34,20 @@ export interface UserProfile { bnsAddress?: string; } -// Transform functions using generic utility -export const transformUserProfileToCamelCase = (profile: UserProfilesTable): UserProfile => toCamelCase(profile); -export const transformUserProfileToSnakeCase = (profile: Partial): Partial> => - toSnakeCase(profile); +// Transform functions with explicit mapping +export const transformUserProfileToCamelCase = (profile: UserProfilesTable): UserProfile => ({ + id: profile.id, + createdAt: profile.created_at || '', + updatedAt: profile.updated_at || '', + userRole: profile.user_role, + accountIndex: profile.account_index, + stxAddress: profile.stx_address, + bnsAddress: profile.bns_address +}); + +export const transformUserProfileToSnakeCase = (profile: Partial): Partial> => ({ + user_role: profile.userRole, + account_index: profile.accountIndex, + stx_address: profile.stxAddress, + bns_address: profile.bnsAddress +}); diff --git a/src/database/models/UserTasks.ts b/src/database/models/UserTasks.ts index dddeac9..f4dcd8d 100644 --- a/src/database/models/UserTasks.ts +++ b/src/database/models/UserTasks.ts @@ -37,7 +37,24 @@ export interface UserTask { taskExpectedOutput: string; } -// Transform functions using generic utility -export const transformUserTaskToCamelCase = (task: UserTasksTable): UserTask => toCamelCase(task); -export const transformUserTaskToSnakeCase = (task: Partial): Partial> => - toSnakeCase(task); +// Transform functions with explicit mapping +export const transformUserTaskToCamelCase = (task: UserTasksTable): UserTask => ({ + id: task.id, + createdAt: task.created_at || '', + updatedAt: task.updated_at || '', + profileId: task.profile_id, + crewId: task.crew_id, + agentId: task.agent_id, + taskName: task.task_name, + taskDescription: task.task_description, + taskExpectedOutput: task.task_expected_output +}); + +export const transformUserTaskToSnakeCase = (task: Partial): Partial> => ({ + profile_id: task.profileId, + crew_id: task.crewId, + agent_id: task.agentId, + task_name: task.taskName, + task_description: task.taskDescription, + task_expected_output: task.taskExpectedOutput +}); diff --git a/src/database/models/XBotAuthors.ts b/src/database/models/XBotAuthors.ts index 998550d..e52419a 100644 --- a/src/database/models/XBotAuthors.ts +++ b/src/database/models/XBotAuthors.ts @@ -31,7 +31,18 @@ export interface XBotAuthor { username?: string; } -// Transform functions using generic utility -export const transformXBotAuthorToCamelCase = (author: XBotAuthorsTable): XBotAuthor => toCamelCase(author); -export const transformXBotAuthorToSnakeCase = (author: Partial): Partial> => - toSnakeCase(author); +// Transform functions with explicit mapping +export const transformXBotAuthorToCamelCase = (author: XBotAuthorsTable): XBotAuthor => ({ + id: author.id, + createdAt: author.created_at || '', + updatedAt: author.updated_at || '', + authorId: author.author_id, + realname: author.realname, + username: author.username +}); + +export const transformXBotAuthorToSnakeCase = (author: Partial): Partial> => ({ + author_id: author.authorId, + realname: author.realname, + username: author.username +}); diff --git a/src/database/models/XBotLogs.ts b/src/database/models/XBotLogs.ts index ade6683..6c84f17 100644 --- a/src/database/models/XBotLogs.ts +++ b/src/database/models/XBotLogs.ts @@ -31,7 +31,18 @@ export interface XBotLog { logMessage?: string; } -// Transform functions using generic utility -export const transformXBotLogToCamelCase = (log: XBotLogsTable): XBotLog => toCamelCase(log); -export const transformXBotLogToSnakeCase = (log: Partial): Partial> => - toSnakeCase(log); +// Transform functions with explicit mapping +export const transformXBotLogToCamelCase = (log: XBotLogsTable): XBotLog => ({ + id: log.id, + createdAt: log.created_at || '', + updatedAt: log.updated_at || '', + tweetId: log.tweet_id, + tweetStatus: log.tweet_status, + logMessage: log.log_message +}); + +export const transformXBotLogToSnakeCase = (log: Partial): Partial> => ({ + tweet_id: log.tweetId, + tweet_status: log.tweetStatus, + log_message: log.logMessage +}); diff --git a/src/database/models/XBotThreads.ts b/src/database/models/XBotThreads.ts index ca6c532..c04f5c4 100644 --- a/src/database/models/XBotThreads.ts +++ b/src/database/models/XBotThreads.ts @@ -25,7 +25,11 @@ export interface XBotThread { updatedAt: string; } -// Transform functions using generic utility -export const transformXBotThreadToCamelCase = (thread: XBotThreadsTable): XBotThread => toCamelCase(thread); -export const transformXBotThreadToSnakeCase = (thread: Partial): Partial> => - toSnakeCase(thread); +// Transform functions with explicit mapping +export const transformXBotThreadToCamelCase = (thread: XBotThreadsTable): XBotThread => ({ + id: thread.id, + createdAt: thread.created_at || '', + updatedAt: thread.updated_at || '' +}); + +export const transformXBotThreadToSnakeCase = (thread: Partial): Partial> => ({}); diff --git a/src/database/models/XBotTweets.ts b/src/database/models/XBotTweets.ts index 5c57333..708ae67 100644 --- a/src/database/models/XBotTweets.ts +++ b/src/database/models/XBotTweets.ts @@ -41,7 +41,28 @@ export interface XBotTweet { isBotResponse?: boolean; } -// Transform functions using generic utility -export const transformXBotTweetToCamelCase = (tweet: XBotTweetsTable): XBotTweet => toCamelCase(tweet); -export const transformXBotTweetToSnakeCase = (tweet: Partial): Partial> => - toSnakeCase(tweet); +// Transform functions with explicit mapping +export const transformXBotTweetToCamelCase = (tweet: XBotTweetsTable): XBotTweet => ({ + id: tweet.id, + createdAt: tweet.created_at || '', + updatedAt: tweet.updated_at || '', + authorId: tweet.author_id, + threadId: tweet.thread_id, + parentTweetId: tweet.parent_tweet_id, + tweetId: tweet.tweet_id, + tweetCreatedAt: tweet.tweet_created_at, + tweetUpdatedAt: tweet.tweet_updated_at, + tweetBody: tweet.tweet_body, + isBotResponse: tweet.is_bot_response +}); + +export const transformXBotTweetToSnakeCase = (tweet: Partial): Partial> => ({ + author_id: tweet.authorId, + thread_id: tweet.threadId, + parent_tweet_id: tweet.parentTweetId, + tweet_id: tweet.tweetId, + tweet_created_at: tweet.tweetCreatedAt, + tweet_updated_at: tweet.tweetUpdatedAt, + tweet_body: tweet.tweetBody, + is_bot_response: tweet.isBotResponse +}); From ccb30021bce1496c805f02977f2c6add6b59bc8a Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:51:51 -0700 Subject: [PATCH 122/128] refactor: Remove unused case transformer imports and utility file --- src/database/models/UserProfile.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/database/models/UserProfile.ts b/src/database/models/UserProfile.ts index 2949839..81f882d 100644 --- a/src/database/models/UserProfile.ts +++ b/src/database/models/UserProfile.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userProfilesModel = new Model( { From ac6f5a66ac50128a7f6ebe5702c6a21d3d738e72 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:52:17 -0700 Subject: [PATCH 123/128] refactor: Replace generic case transformers with explicit mapping --- src/database/models/UserCrewExecutions.ts | 27 +++++++++++++++++++---- src/database/models/UserSocials.ts | 19 ++++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/database/models/UserCrewExecutions.ts b/src/database/models/UserCrewExecutions.ts index 12eb25e..b6563ea 100644 --- a/src/database/models/UserCrewExecutions.ts +++ b/src/database/models/UserCrewExecutions.ts @@ -39,7 +39,26 @@ export interface UserCrewExecution { successfulRequests?: number; } -// Transform functions using generic utility -export const transformUserCrewExecutionToCamelCase = (execution: UserCrewExecutionsTable): UserCrewExecution => toCamelCase(execution); -export const transformUserCrewExecutionToSnakeCase = (execution: Partial): Partial> => - toSnakeCase(execution); +// Transform functions with explicit mapping +export const transformUserCrewExecutionToCamelCase = (execution: UserCrewExecutionsTable): UserCrewExecution => ({ + id: execution.id, + createdAt: execution.created_at || '', + updatedAt: execution.updated_at || '', + profileId: execution.profile_id, + crewId: execution.crew_id, + conversationId: execution.conversation_id, + userInput: execution.user_input, + finalResult: execution.final_result, + totalTokens: execution.total_tokens, + successfulRequests: execution.successful_requests +}); + +export const transformUserCrewExecutionToSnakeCase = (execution: Partial): Partial> => ({ + profile_id: execution.profileId, + crew_id: execution.crewId, + conversation_id: execution.conversationId, + user_input: execution.userInput, + final_result: execution.finalResult, + total_tokens: execution.totalTokens, + successful_requests: execution.successfulRequests +}); diff --git a/src/database/models/UserSocials.ts b/src/database/models/UserSocials.ts index 3435e55..f5a583b 100644 --- a/src/database/models/UserSocials.ts +++ b/src/database/models/UserSocials.ts @@ -31,7 +31,18 @@ export interface UserSocial { platformId: string; } -// Transform functions using generic utility -export const transformUserSocialToCamelCase = (social: UserSocialsTable): UserSocial => toCamelCase(social); -export const transformUserSocialToSnakeCase = (social: Partial): Partial> => - toSnakeCase(social); +// Transform functions with explicit mapping +export const transformUserSocialToCamelCase = (social: UserSocialsTable): UserSocial => ({ + id: social.id, + createdAt: social.created_at || '', + updatedAt: social.updated_at || '', + profileId: social.profile_id, + platform: social.platform, + platformId: social.platform_id +}); + +export const transformUserSocialToSnakeCase = (social: Partial): Partial> => ({ + profile_id: social.profileId, + platform: social.platform, + platform_id: social.platformId +}); From b4cefbc88b466cfeb8cfc967d52b802951cb2d54 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Mon, 23 Dec 2024 15:52:46 -0700 Subject: [PATCH 124/128] fix: remove generic utility, unused after changes --- src/database/models/UserAgents.ts | 38 ++++++++++++++++--------------- src/utils/case-transformers.ts | 37 ------------------------------ 2 files changed, 20 insertions(+), 55 deletions(-) delete mode 100644 src/utils/case-transformers.ts diff --git a/src/database/models/UserAgents.ts b/src/database/models/UserAgents.ts index a6b72fd..e32f400 100644 --- a/src/database/models/UserAgents.ts +++ b/src/database/models/UserAgents.ts @@ -41,24 +41,26 @@ export interface UserAgent { // Transform functions using generic utility export const transformUserAgentToCamelCase = (agent: UserAgentsTable): UserAgent => ({ - id: agent.id, - createdAt: agent.created_at || '', - updatedAt: agent.updated_at || '', - profileId: agent.profile_id, - crewId: agent.crew_id, - agentName: agent.agent_name, - agentRole: agent.agent_role, - agentGoal: agent.agent_goal, - agentBackstory: agent.agent_backstory, - agentTools: agent.agent_tools, + id: agent.id, + createdAt: agent.created_at || '', + updatedAt: agent.updated_at || '', + profileId: agent.profile_id, + crewId: agent.crew_id, + agentName: agent.agent_name, + agentRole: agent.agent_role, + agentGoal: agent.agent_goal, + agentBackstory: agent.agent_backstory, + agentTools: agent.agent_tools ?? undefined, }); -export const transformUserAgentToSnakeCase = (agent: Partial): Partial> => ({ - profile_id: agent.profileId, - crew_id: agent.crewId, - agent_name: agent.agentName, - agent_role: agent.agentRole, - agent_goal: agent.agentGoal, - agent_backstory: agent.agentBackstory, - agent_tools: agent.agentTools, +export const transformUserAgentToSnakeCase = ( + agent: Partial +): Partial> => ({ + profile_id: agent.profileId, + crew_id: agent.crewId, + agent_name: agent.agentName, + agent_role: agent.agentRole, + agent_goal: agent.agentGoal, + agent_backstory: agent.agentBackstory, + agent_tools: agent.agentTools, }); diff --git a/src/utils/case-transformers.ts b/src/utils/case-transformers.ts deleted file mode 100644 index 88d826c..0000000 --- a/src/utils/case-transformers.ts +++ /dev/null @@ -1,37 +0,0 @@ -type CamelToSnakeCase = S extends `${infer T}${infer U}` - ? `${T extends Capitalize ? '_' : ''}${Lowercase}${CamelToSnakeCase}` - : S; - -type SnakeToCamelCase = S extends `${infer T}_${infer U}` - ? `${T}${Capitalize>}` - : S; - -type CamelToSnake = { - [K in keyof T as CamelToSnakeCase]: T[K]; -}; - -type SnakeToCamel = { - [K in keyof T as SnakeToCamelCase]: T[K]; -}; - -export const toCamelCase = >(obj: T): SnakeToCamel => { - const camelObj: any = {}; - Object.keys(obj).forEach((key) => { - if (obj[key] !== undefined) { - const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); - camelObj[camelKey] = obj[key]; - } - }); - return camelObj; -}; - -export const toSnakeCase = >(obj: T): CamelToSnake => { - const snakeObj: any = {}; - Object.keys(obj).forEach((key) => { - if (obj[key] !== undefined) { - const snakeKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`); - snakeObj[snakeKey] = obj[key]; - } - }); - return snakeObj; -}; From 9c2975db28e2f97ba7d730bc09e40410e6e96cdb Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 15:53:24 -0700 Subject: [PATCH 125/128] refactor: Remove unused case transformer imports from model files --- src/database/models/UserAgents.ts | 1 - src/database/models/UserConversations.ts | 1 - src/database/models/UserCrewExecutionSteps.ts | 1 - src/database/models/UserCrews.ts | 1 - src/database/models/UserCrons.ts | 1 - src/database/models/UserTasks.ts | 1 - src/database/models/XBotAuthors.ts | 1 - src/database/models/XBotLogs.ts | 1 - src/database/models/XBotThreads.ts | 1 - src/database/models/XBotTweets.ts | 1 - 10 files changed, 10 deletions(-) diff --git a/src/database/models/UserAgents.ts b/src/database/models/UserAgents.ts index e32f400..bc43e38 100644 --- a/src/database/models/UserAgents.ts +++ b/src/database/models/UserAgents.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userAgentsModel = new Model( { diff --git a/src/database/models/UserConversations.ts b/src/database/models/UserConversations.ts index c3757e1..22cc5df 100644 --- a/src/database/models/UserConversations.ts +++ b/src/database/models/UserConversations.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userConversationsModel = new Model( { diff --git a/src/database/models/UserCrewExecutionSteps.ts b/src/database/models/UserCrewExecutionSteps.ts index 0c75106..fb21d7c 100644 --- a/src/database/models/UserCrewExecutionSteps.ts +++ b/src/database/models/UserCrewExecutionSteps.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userCrewExecutionStepsModel = new Model( { diff --git a/src/database/models/UserCrews.ts b/src/database/models/UserCrews.ts index 90fd41f..6b3fd2a 100644 --- a/src/database/models/UserCrews.ts +++ b/src/database/models/UserCrews.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userCrewsModel = new Model( { diff --git a/src/database/models/UserCrons.ts b/src/database/models/UserCrons.ts index 69641df..a99a75e 100644 --- a/src/database/models/UserCrons.ts +++ b/src/database/models/UserCrons.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userCronsModel = new Model( { diff --git a/src/database/models/UserTasks.ts b/src/database/models/UserTasks.ts index f4dcd8d..63bba3c 100644 --- a/src/database/models/UserTasks.ts +++ b/src/database/models/UserTasks.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userTasksModel = new Model( { diff --git a/src/database/models/XBotAuthors.ts b/src/database/models/XBotAuthors.ts index e52419a..05255f9 100644 --- a/src/database/models/XBotAuthors.ts +++ b/src/database/models/XBotAuthors.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const xBotAuthorsModel = new Model( { diff --git a/src/database/models/XBotLogs.ts b/src/database/models/XBotLogs.ts index 6c84f17..ca0c0ae 100644 --- a/src/database/models/XBotLogs.ts +++ b/src/database/models/XBotLogs.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const xBotLogsModel = new Model( { diff --git a/src/database/models/XBotThreads.ts b/src/database/models/XBotThreads.ts index c04f5c4..23e8326 100644 --- a/src/database/models/XBotThreads.ts +++ b/src/database/models/XBotThreads.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const xBotThreadsModel = new Model( { diff --git a/src/database/models/XBotTweets.ts b/src/database/models/XBotTweets.ts index 708ae67..0971af9 100644 --- a/src/database/models/XBotTweets.ts +++ b/src/database/models/XBotTweets.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const xBotTweetsModel = new Model( { From 6437997fe63bce8f77b8798954202764e90dc093 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Mon, 23 Dec 2024 16:01:43 -0700 Subject: [PATCH 126/128] refactor: Improve null coalescing and optional handling in model transformations --- src/database/models/UserCrews.ts | 52 +++++++-------- src/database/models/XBotTweets.ts | 102 +++++++++++++++--------------- 2 files changed, 79 insertions(+), 75 deletions(-) diff --git a/src/database/models/UserCrews.ts b/src/database/models/UserCrews.ts index 6b3fd2a..a3c0f14 100644 --- a/src/database/models/UserCrews.ts +++ b/src/database/models/UserCrews.ts @@ -25,35 +25,37 @@ export type UserCrewsTable = Infer; // CamelCase interface for application use export interface UserCrew { - id: number; - createdAt: string; - updatedAt: string; - profileId: string; - crewName: string; - crewDescription?: string; - crewExecutions?: number; - crewIsPublic?: boolean; - crewIsCron?: boolean; + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + crewName: string; + crewDescription?: string; + crewExecutions?: number; + crewIsPublic?: boolean; + crewIsCron?: boolean; } // Transform functions with explicit mapping export const transformUserCrewToCamelCase = (crew: UserCrewsTable): UserCrew => ({ - id: crew.id, - createdAt: crew.created_at || '', - updatedAt: crew.updated_at || '', - profileId: crew.profile_id, - crewName: crew.crew_name, - crewDescription: crew.crew_description, - crewExecutions: crew.crew_executions, - crewIsPublic: crew.crew_is_public, - crewIsCron: crew.crew_is_cron + id: crew.id, + createdAt: crew.created_at || '', + updatedAt: crew.updated_at || '', + profileId: crew.profile_id, + crewName: crew.crew_name, + crewDescription: crew.crew_description ?? undefined, + crewExecutions: crew.crew_executions ?? undefined, + crewIsPublic: crew.crew_is_public ?? undefined, + crewIsCron: crew.crew_is_cron ?? undefined, }); -export const transformUserCrewToSnakeCase = (crew: Partial): Partial> => ({ - profile_id: crew.profileId, - crew_name: crew.crewName, - crew_description: crew.crewDescription, - crew_executions: crew.crewExecutions, - crew_is_public: crew.crewIsPublic, - crew_is_cron: crew.crewIsCron +export const transformUserCrewToSnakeCase = ( + crew: Partial +): Partial> => ({ + profile_id: crew.profileId, + crew_name: crew.crewName, + crew_description: crew.crewDescription, + crew_executions: crew.crewExecutions, + crew_is_public: crew.crewIsPublic ?? undefined, + crew_is_cron: crew.crewIsCron ?? undefined, }); diff --git a/src/database/models/XBotTweets.ts b/src/database/models/XBotTweets.ts index 0971af9..ef356c1 100644 --- a/src/database/models/XBotTweets.ts +++ b/src/database/models/XBotTweets.ts @@ -1,25 +1,25 @@ import { Model, DataTypes, Infer } from 'd1-orm'; export const xBotTweetsModel = new Model( - { - D1Orm: undefined, - tableName: 'x_bot_tweets', - primaryKeys: 'id', - autoIncrement: 'id', - }, - { - id: { type: DataTypes.INTEGER, notNull: true }, - created_at: { type: DataTypes.STRING }, - updated_at: { type: DataTypes.STRING }, - author_id: { type: DataTypes.STRING, notNull: true }, - thread_id: { type: DataTypes.INTEGER }, - parent_tweet_id: { type: DataTypes.STRING }, - tweet_id: { type: DataTypes.STRING, notNull: true }, - tweet_created_at: { type: DataTypes.STRING }, - tweet_updated_at: { type: DataTypes.STRING }, - tweet_body: { type: DataTypes.STRING }, - is_bot_response: { type: DataTypes.BOOLEAN }, - } + { + D1Orm: undefined, + tableName: 'x_bot_tweets', + primaryKeys: 'id', + autoIncrement: 'id', + }, + { + id: { type: DataTypes.INTEGER, notNull: true }, + created_at: { type: DataTypes.STRING }, + updated_at: { type: DataTypes.STRING }, + author_id: { type: DataTypes.STRING, notNull: true }, + thread_id: { type: DataTypes.INTEGER }, + parent_tweet_id: { type: DataTypes.STRING }, + tweet_id: { type: DataTypes.STRING, notNull: true }, + tweet_created_at: { type: DataTypes.STRING }, + tweet_updated_at: { type: DataTypes.STRING }, + tweet_body: { type: DataTypes.STRING }, + is_bot_response: { type: DataTypes.BOOLEAN }, + } ); // Original type for ORM operations @@ -27,41 +27,43 @@ export type XBotTweetsTable = Infer; // CamelCase interface for application use export interface XBotTweet { - id: number; - createdAt: string; - updatedAt: string; - authorId: string; - threadId?: number; - parentTweetId?: string; - tweetId: string; - tweetCreatedAt?: string; - tweetUpdatedAt?: string; - tweetBody?: string; - isBotResponse?: boolean; + id: number; + createdAt: string; + updatedAt: string; + authorId: string; + threadId?: number; + parentTweetId?: string; + tweetId: string; + tweetCreatedAt?: string; + tweetUpdatedAt?: string; + tweetBody?: string; + isBotResponse?: boolean; } // Transform functions with explicit mapping export const transformXBotTweetToCamelCase = (tweet: XBotTweetsTable): XBotTweet => ({ - id: tweet.id, - createdAt: tweet.created_at || '', - updatedAt: tweet.updated_at || '', - authorId: tweet.author_id, - threadId: tweet.thread_id, - parentTweetId: tweet.parent_tweet_id, - tweetId: tweet.tweet_id, - tweetCreatedAt: tweet.tweet_created_at, - tweetUpdatedAt: tweet.tweet_updated_at, - tweetBody: tweet.tweet_body, - isBotResponse: tweet.is_bot_response + id: tweet.id, + createdAt: tweet.created_at || '', + updatedAt: tweet.updated_at || '', + authorId: tweet.author_id, + threadId: tweet.thread_id ?? undefined, + parentTweetId: tweet.parent_tweet_id ?? undefined, + tweetId: tweet.tweet_id, + tweetCreatedAt: tweet.tweet_created_at ?? undefined, + tweetUpdatedAt: tweet.tweet_updated_at ?? undefined, + tweetBody: tweet.tweet_body ?? undefined, + isBotResponse: tweet.is_bot_response, }); -export const transformXBotTweetToSnakeCase = (tweet: Partial): Partial> => ({ - author_id: tweet.authorId, - thread_id: tweet.threadId, - parent_tweet_id: tweet.parentTweetId, - tweet_id: tweet.tweetId, - tweet_created_at: tweet.tweetCreatedAt, - tweet_updated_at: tweet.tweetUpdatedAt, - tweet_body: tweet.tweetBody, - is_bot_response: tweet.isBotResponse +export const transformXBotTweetToSnakeCase = ( + tweet: Partial +): Partial> => ({ + author_id: tweet.authorId, + thread_id: tweet.threadId, + parent_tweet_id: tweet.parentTweetId, + tweet_id: tweet.tweetId, + tweet_created_at: tweet.tweetCreatedAt, + tweet_updated_at: tweet.tweetUpdatedAt, + tweet_body: tweet.tweetBody, + is_bot_response: tweet.isBotResponse, }); From e42a0cf623743b02475a0377060b35533f4c76ab Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Mon, 23 Dec 2024 16:01:45 -0700 Subject: [PATCH 127/128] fix: Improve boolean conversion in database model transformations --- src/database/models/UserCrews.ts | 8 ++++---- src/database/models/UserCrons.ts | 4 ++-- src/database/models/XBotTweets.ts | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/database/models/UserCrews.ts b/src/database/models/UserCrews.ts index a3c0f14..3507b73 100644 --- a/src/database/models/UserCrews.ts +++ b/src/database/models/UserCrews.ts @@ -45,8 +45,8 @@ export const transformUserCrewToCamelCase = (crew: UserCrewsTable): UserCrew => crewName: crew.crew_name, crewDescription: crew.crew_description ?? undefined, crewExecutions: crew.crew_executions ?? undefined, - crewIsPublic: crew.crew_is_public ?? undefined, - crewIsCron: crew.crew_is_cron ?? undefined, + crewIsPublic: crew.crew_is_public !== null ? Boolean(crew.crew_is_public) : undefined, + crewIsCron: crew.crew_is_cron !== null ? Boolean(crew.crew_is_cron) : undefined, }); export const transformUserCrewToSnakeCase = ( @@ -56,6 +56,6 @@ export const transformUserCrewToSnakeCase = ( crew_name: crew.crewName, crew_description: crew.crewDescription, crew_executions: crew.crewExecutions, - crew_is_public: crew.crewIsPublic ?? undefined, - crew_is_cron: crew.crewIsCron ?? undefined, + crew_is_public: crew.crewIsPublic !== undefined ? (crew.crewIsPublic ? 1 : 0) : undefined, + crew_is_cron: crew.crewIsCron !== undefined ? (crew.crewIsCron ? 1 : 0) : undefined, }); diff --git a/src/database/models/UserCrons.ts b/src/database/models/UserCrons.ts index a99a75e..7ced8ef 100644 --- a/src/database/models/UserCrons.ts +++ b/src/database/models/UserCrons.ts @@ -41,7 +41,7 @@ export const transformUserCronToCamelCase = (cron: UserCronsTable): UserCron => updatedAt: cron.updated_at || '', profileId: cron.profile_id, crewId: cron.crew_id, - cronEnabled: cron.cron_enabled, + cronEnabled: Boolean(cron.cron_enabled), cronInterval: cron.cron_interval, cronInput: cron.cron_input }); @@ -49,7 +49,7 @@ export const transformUserCronToCamelCase = (cron: UserCronsTable): UserCron => export const transformUserCronToSnakeCase = (cron: Partial): Partial> => ({ profile_id: cron.profileId, crew_id: cron.crewId, - cron_enabled: cron.cronEnabled, + cron_enabled: cron.cronEnabled ? 1 : 0, cron_interval: cron.cronInterval, cron_input: cron.cronInput }); diff --git a/src/database/models/XBotTweets.ts b/src/database/models/XBotTweets.ts index ef356c1..bc62dce 100644 --- a/src/database/models/XBotTweets.ts +++ b/src/database/models/XBotTweets.ts @@ -52,7 +52,7 @@ export const transformXBotTweetToCamelCase = (tweet: XBotTweetsTable): XBotTweet tweetCreatedAt: tweet.tweet_created_at ?? undefined, tweetUpdatedAt: tweet.tweet_updated_at ?? undefined, tweetBody: tweet.tweet_body ?? undefined, - isBotResponse: tweet.is_bot_response, + isBotResponse: Boolean(tweet.is_bot_response), }); export const transformXBotTweetToSnakeCase = ( @@ -65,5 +65,5 @@ export const transformXBotTweetToSnakeCase = ( tweet_created_at: tweet.tweetCreatedAt, tweet_updated_at: tweet.tweetUpdatedAt, tweet_body: tweet.tweetBody, - is_bot_response: tweet.isBotResponse, + is_bot_response: tweet.isBotResponse ? 1 : 0, }); From 486f2cea78fc0066cb5d292ab2fcc6ec76bfe269 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Mon, 23 Dec 2024 16:02:19 -0700 Subject: [PATCH 128/128] human intervention: cleanup and consistency --- src/database/models/UserConversations.ts | 28 +++++---- src/database/models/UserCrewExecutionSteps.ts | 30 ++++----- src/database/models/UserCrewExecutions.ts | 59 +++++++++--------- src/database/models/UserProfile.ts | 40 ++++++------ src/database/models/UserSocials.ts | 35 ++++++----- src/database/models/UserTasks.ts | 52 ++++++++-------- src/database/models/XBotAuthors.ts | 62 ++++++++++--------- src/database/models/XBotLogs.ts | 58 ++++++++--------- src/database/models/XBotThreads.ts | 38 ++++++------ 9 files changed, 208 insertions(+), 194 deletions(-) diff --git a/src/database/models/UserConversations.ts b/src/database/models/UserConversations.ts index 22cc5df..517906b 100644 --- a/src/database/models/UserConversations.ts +++ b/src/database/models/UserConversations.ts @@ -21,23 +21,25 @@ export type UserConversationsTable = Infer; // CamelCase interface for application use export interface UserConversation { - id: number; - createdAt: string; - updatedAt: string; - profileId: string; - conversationName: string; + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + conversationName: string; } // Transform functions with explicit mapping export const transformUserConversationToCamelCase = (conversation: UserConversationsTable): UserConversation => ({ - id: conversation.id, - createdAt: conversation.created_at || '', - updatedAt: conversation.updated_at || '', - profileId: conversation.profile_id, - conversationName: conversation.conversation_name + id: conversation.id, + createdAt: conversation.created_at || '', + updatedAt: conversation.updated_at || '', + profileId: conversation.profile_id, + conversationName: conversation.conversation_name, }); -export const transformUserConversationToSnakeCase = (conversation: Partial): Partial> => ({ - profile_id: conversation.profileId, - conversation_name: conversation.conversationName +export const transformUserConversationToSnakeCase = ( + conversation: Partial +): Partial> => ({ + profile_id: conversation.profileId, + conversation_name: conversation.conversationName, }); diff --git a/src/database/models/UserCrewExecutionSteps.ts b/src/database/models/UserCrewExecutionSteps.ts index fb21d7c..4ea3e4a 100644 --- a/src/database/models/UserCrewExecutionSteps.ts +++ b/src/database/models/UserCrewExecutionSteps.ts @@ -36,20 +36,22 @@ export interface UserCrewExecutionStep { // Transform functions with explicit mapping export const transformUserCrewExecutionStepToCamelCase = (step: UserCrewExecutionStepsTable): UserCrewExecutionStep => ({ - id: step.id, - createdAt: step.created_at || '', - updatedAt: step.updated_at || '', - profileId: step.profile_id, - crewId: step.crew_id, - executionId: step.execution_id, - stepType: step.step_type, - stepData: step.step_data + id: step.id, + createdAt: step.created_at || '', + updatedAt: step.updated_at || '', + profileId: step.profile_id, + crewId: step.crew_id, + executionId: step.execution_id, + stepType: step.step_type, + stepData: step.step_data, }); -export const transformUserCrewExecutionStepToSnakeCase = (step: Partial): Partial> => ({ - profile_id: step.profileId, - crew_id: step.crewId, - execution_id: step.executionId, - step_type: step.stepType, - step_data: step.stepData +export const transformUserCrewExecutionStepToSnakeCase = ( + step: Partial +): Partial> => ({ + profile_id: step.profileId, + crew_id: step.crewId, + execution_id: step.executionId, + step_type: step.stepType, + step_data: step.stepData, }); diff --git a/src/database/models/UserCrewExecutions.ts b/src/database/models/UserCrewExecutions.ts index b6563ea..ae5a74d 100644 --- a/src/database/models/UserCrewExecutions.ts +++ b/src/database/models/UserCrewExecutions.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userCrewExecutionsModel = new Model( { @@ -27,38 +26,40 @@ export type UserCrewExecutionsTable = Infer; // CamelCase interface for application use export interface UserCrewExecution { - id: number; - createdAt: string; - updatedAt: string; - profileId: string; - crewId: number; - conversationId: number; - userInput?: string; - finalResult?: string; - totalTokens?: number; - successfulRequests?: number; + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + crewId: number; + conversationId: number; + userInput?: string; + finalResult?: string; + totalTokens?: number; + successfulRequests?: number; } // Transform functions with explicit mapping export const transformUserCrewExecutionToCamelCase = (execution: UserCrewExecutionsTable): UserCrewExecution => ({ - id: execution.id, - createdAt: execution.created_at || '', - updatedAt: execution.updated_at || '', - profileId: execution.profile_id, - crewId: execution.crew_id, - conversationId: execution.conversation_id, - userInput: execution.user_input, - finalResult: execution.final_result, - totalTokens: execution.total_tokens, - successfulRequests: execution.successful_requests + id: execution.id, + createdAt: execution.created_at || '', + updatedAt: execution.updated_at || '', + profileId: execution.profile_id, + crewId: execution.crew_id, + conversationId: execution.conversation_id, + userInput: execution.user_input ?? undefined, + finalResult: execution.final_result ?? undefined, + totalTokens: execution.total_tokens ?? undefined, + successfulRequests: execution.successful_requests ?? undefined, }); -export const transformUserCrewExecutionToSnakeCase = (execution: Partial): Partial> => ({ - profile_id: execution.profileId, - crew_id: execution.crewId, - conversation_id: execution.conversationId, - user_input: execution.userInput, - final_result: execution.finalResult, - total_tokens: execution.totalTokens, - successful_requests: execution.successfulRequests +export const transformUserCrewExecutionToSnakeCase = ( + execution: Partial +): Partial> => ({ + profile_id: execution.profileId, + crew_id: execution.crewId, + conversation_id: execution.conversationId, + user_input: execution.userInput, + final_result: execution.finalResult, + total_tokens: execution.totalTokens, + successful_requests: execution.successfulRequests, }); diff --git a/src/database/models/UserProfile.ts b/src/database/models/UserProfile.ts index 81f882d..bf3c465 100644 --- a/src/database/models/UserProfile.ts +++ b/src/database/models/UserProfile.ts @@ -24,29 +24,31 @@ export type UserProfilesTable = Infer; // CamelCase interface for application use export interface UserProfile { - id: number; - createdAt: string; - updatedAt: string; - userRole: string; - accountIndex?: number; - stxAddress: string; - bnsAddress?: string; + id: number; + createdAt: string; + updatedAt: string; + userRole: string; + accountIndex?: number; + stxAddress: string; + bnsAddress?: string; } // Transform functions with explicit mapping export const transformUserProfileToCamelCase = (profile: UserProfilesTable): UserProfile => ({ - id: profile.id, - createdAt: profile.created_at || '', - updatedAt: profile.updated_at || '', - userRole: profile.user_role, - accountIndex: profile.account_index, - stxAddress: profile.stx_address, - bnsAddress: profile.bns_address + id: profile.id, + createdAt: profile.created_at || '', + updatedAt: profile.updated_at || '', + userRole: profile.user_role, + accountIndex: profile.account_index ?? undefined, + stxAddress: profile.stx_address, + bnsAddress: profile.bns_address ?? undefined, }); -export const transformUserProfileToSnakeCase = (profile: Partial): Partial> => ({ - user_role: profile.userRole, - account_index: profile.accountIndex, - stx_address: profile.stxAddress, - bns_address: profile.bnsAddress +export const transformUserProfileToSnakeCase = ( + profile: Partial +): Partial> => ({ + user_role: profile.userRole, + account_index: profile.accountIndex, + stx_address: profile.stxAddress, + bns_address: profile.bnsAddress, }); diff --git a/src/database/models/UserSocials.ts b/src/database/models/UserSocials.ts index f5a583b..22c8aaa 100644 --- a/src/database/models/UserSocials.ts +++ b/src/database/models/UserSocials.ts @@ -1,5 +1,4 @@ import { Model, DataTypes, Infer } from 'd1-orm'; -import { toCamelCase, toSnakeCase } from '../../utils/case-transformers'; export const userSocialsModel = new Model( { @@ -23,26 +22,28 @@ export type UserSocialsTable = Infer; // CamelCase interface for application use export interface UserSocial { - id: number; - createdAt: string; - updatedAt: string; - profileId: string; - platform: string; - platformId: string; + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + platform: string; + platformId: string; } // Transform functions with explicit mapping export const transformUserSocialToCamelCase = (social: UserSocialsTable): UserSocial => ({ - id: social.id, - createdAt: social.created_at || '', - updatedAt: social.updated_at || '', - profileId: social.profile_id, - platform: social.platform, - platformId: social.platform_id + id: social.id, + createdAt: social.created_at || '', + updatedAt: social.updated_at || '', + profileId: social.profile_id, + platform: social.platform, + platformId: social.platform_id, }); -export const transformUserSocialToSnakeCase = (social: Partial): Partial> => ({ - profile_id: social.profileId, - platform: social.platform, - platform_id: social.platformId +export const transformUserSocialToSnakeCase = ( + social: Partial +): Partial> => ({ + profile_id: social.profileId, + platform: social.platform, + platform_id: social.platformId, }); diff --git a/src/database/models/UserTasks.ts b/src/database/models/UserTasks.ts index 63bba3c..2d0ae2c 100644 --- a/src/database/models/UserTasks.ts +++ b/src/database/models/UserTasks.ts @@ -25,35 +25,37 @@ export type UserTasksTable = Infer; // CamelCase interface for application use export interface UserTask { - id: number; - createdAt: string; - updatedAt: string; - profileId: string; - crewId: number; - agentId: number; - taskName: string; - taskDescription: string; - taskExpectedOutput: string; + id: number; + createdAt: string; + updatedAt: string; + profileId: string; + crewId: number; + agentId: number; + taskName: string; + taskDescription: string; + taskExpectedOutput: string; } // Transform functions with explicit mapping export const transformUserTaskToCamelCase = (task: UserTasksTable): UserTask => ({ - id: task.id, - createdAt: task.created_at || '', - updatedAt: task.updated_at || '', - profileId: task.profile_id, - crewId: task.crew_id, - agentId: task.agent_id, - taskName: task.task_name, - taskDescription: task.task_description, - taskExpectedOutput: task.task_expected_output + id: task.id, + createdAt: task.created_at || '', + updatedAt: task.updated_at || '', + profileId: task.profile_id, + crewId: task.crew_id, + agentId: task.agent_id, + taskName: task.task_name, + taskDescription: task.task_description, + taskExpectedOutput: task.task_expected_output, }); -export const transformUserTaskToSnakeCase = (task: Partial): Partial> => ({ - profile_id: task.profileId, - crew_id: task.crewId, - agent_id: task.agentId, - task_name: task.taskName, - task_description: task.taskDescription, - task_expected_output: task.taskExpectedOutput +export const transformUserTaskToSnakeCase = ( + task: Partial +): Partial> => ({ + profile_id: task.profileId, + crew_id: task.crewId, + agent_id: task.agentId, + task_name: task.taskName, + task_description: task.taskDescription, + task_expected_output: task.taskExpectedOutput, }); diff --git a/src/database/models/XBotAuthors.ts b/src/database/models/XBotAuthors.ts index 05255f9..2c6dab4 100644 --- a/src/database/models/XBotAuthors.ts +++ b/src/database/models/XBotAuthors.ts @@ -1,20 +1,20 @@ import { Model, DataTypes, Infer } from 'd1-orm'; export const xBotAuthorsModel = new Model( - { - D1Orm: undefined, - tableName: 'x_bot_authors', - primaryKeys: 'id', - autoIncrement: 'id', - }, - { - id: { type: DataTypes.INTEGER, notNull: true }, - created_at: { type: DataTypes.STRING }, - updated_at: { type: DataTypes.STRING }, - author_id: { type: DataTypes.STRING, notNull: true }, - realname: { type: DataTypes.STRING }, - username: { type: DataTypes.STRING }, - } + { + D1Orm: undefined, + tableName: 'x_bot_authors', + primaryKeys: 'id', + autoIncrement: 'id', + }, + { + id: { type: DataTypes.INTEGER, notNull: true }, + created_at: { type: DataTypes.STRING }, + updated_at: { type: DataTypes.STRING }, + author_id: { type: DataTypes.STRING, notNull: true }, + realname: { type: DataTypes.STRING }, + username: { type: DataTypes.STRING }, + } ); // Original type for ORM operations @@ -22,26 +22,28 @@ export type XBotAuthorsTable = Infer; // CamelCase interface for application use export interface XBotAuthor { - id: number; - createdAt: string; - updatedAt: string; - authorId: string; - realname?: string; - username?: string; + id: number; + createdAt: string; + updatedAt: string; + authorId: string; + realName?: string; + username?: string; } // Transform functions with explicit mapping export const transformXBotAuthorToCamelCase = (author: XBotAuthorsTable): XBotAuthor => ({ - id: author.id, - createdAt: author.created_at || '', - updatedAt: author.updated_at || '', - authorId: author.author_id, - realname: author.realname, - username: author.username + id: author.id, + createdAt: author.created_at || '', + updatedAt: author.updated_at || '', + authorId: author.author_id, + realName: author.realname ?? undefined, + username: author.username ?? undefined, }); -export const transformXBotAuthorToSnakeCase = (author: Partial): Partial> => ({ - author_id: author.authorId, - realname: author.realname, - username: author.username +export const transformXBotAuthorToSnakeCase = ( + author: Partial +): Partial> => ({ + author_id: author.authorId, + realname: author.realName, + username: author.username, }); diff --git a/src/database/models/XBotLogs.ts b/src/database/models/XBotLogs.ts index ca0c0ae..ae91640 100644 --- a/src/database/models/XBotLogs.ts +++ b/src/database/models/XBotLogs.ts @@ -1,20 +1,20 @@ import { Model, DataTypes, Infer } from 'd1-orm'; export const xBotLogsModel = new Model( - { - D1Orm: undefined, - tableName: 'x_bot_logs', - primaryKeys: 'id', - autoIncrement: 'id', - }, - { - id: { type: DataTypes.INTEGER, notNull: true }, - created_at: { type: DataTypes.STRING }, - updated_at: { type: DataTypes.STRING }, - tweet_id: { type: DataTypes.STRING, notNull: true }, - tweet_status: { type: DataTypes.STRING }, - log_message: { type: DataTypes.STRING }, - } + { + D1Orm: undefined, + tableName: 'x_bot_logs', + primaryKeys: 'id', + autoIncrement: 'id', + }, + { + id: { type: DataTypes.INTEGER, notNull: true }, + created_at: { type: DataTypes.STRING }, + updated_at: { type: DataTypes.STRING }, + tweet_id: { type: DataTypes.STRING, notNull: true }, + tweet_status: { type: DataTypes.STRING }, + log_message: { type: DataTypes.STRING }, + } ); // Original type for ORM operations @@ -22,26 +22,26 @@ export type XBotLogsTable = Infer; // CamelCase interface for application use export interface XBotLog { - id: number; - createdAt: string; - updatedAt: string; - tweetId: string; - tweetStatus?: string; - logMessage?: string; + id: number; + createdAt: string; + updatedAt: string; + tweetId: string; + tweetStatus?: string; + logMessage?: string; } // Transform functions with explicit mapping export const transformXBotLogToCamelCase = (log: XBotLogsTable): XBotLog => ({ - id: log.id, - createdAt: log.created_at || '', - updatedAt: log.updated_at || '', - tweetId: log.tweet_id, - tweetStatus: log.tweet_status, - logMessage: log.log_message + id: log.id, + createdAt: log.created_at || '', + updatedAt: log.updated_at || '', + tweetId: log.tweet_id, + tweetStatus: log.tweet_status ?? undefined, + logMessage: log.log_message ?? undefined, }); export const transformXBotLogToSnakeCase = (log: Partial): Partial> => ({ - tweet_id: log.tweetId, - tweet_status: log.tweetStatus, - log_message: log.logMessage + tweet_id: log.tweetId, + tweet_status: log.tweetStatus, + log_message: log.logMessage, }); diff --git a/src/database/models/XBotThreads.ts b/src/database/models/XBotThreads.ts index 23e8326..67b265e 100644 --- a/src/database/models/XBotThreads.ts +++ b/src/database/models/XBotThreads.ts @@ -1,17 +1,17 @@ import { Model, DataTypes, Infer } from 'd1-orm'; export const xBotThreadsModel = new Model( - { - D1Orm: undefined, - tableName: 'x_bot_threads', - primaryKeys: 'id', - autoIncrement: 'id', - }, - { - id: { type: DataTypes.INTEGER, notNull: true }, - created_at: { type: DataTypes.STRING }, - updated_at: { type: DataTypes.STRING }, - } + { + D1Orm: undefined, + tableName: 'x_bot_threads', + primaryKeys: 'id', + autoIncrement: 'id', + }, + { + id: { type: DataTypes.INTEGER, notNull: true }, + created_at: { type: DataTypes.STRING }, + updated_at: { type: DataTypes.STRING }, + } ); // Original type for ORM operations @@ -19,16 +19,18 @@ export type XBotThreadsTable = Infer; // CamelCase interface for application use export interface XBotThread { - id: number; - createdAt: string; - updatedAt: string; + id: number; + createdAt: string; + updatedAt: string; } // Transform functions with explicit mapping export const transformXBotThreadToCamelCase = (thread: XBotThreadsTable): XBotThread => ({ - id: thread.id, - createdAt: thread.created_at || '', - updatedAt: thread.updated_at || '' + id: thread.id, + createdAt: thread.created_at || '', + updatedAt: thread.updated_at || '', }); -export const transformXBotThreadToSnakeCase = (thread: Partial): Partial> => ({}); +export const transformXBotThreadToSnakeCase = ( + thread: Partial +): Partial> => ({});