refactor: platform

This commit is contained in:
Acbox
2026-01-15 15:24:40 +08:00
parent a61a1e76b2
commit afc6dc6cb1
15 changed files with 286 additions and 270 deletions
+2
View File
@@ -21,6 +21,8 @@
"@memoh/db": "workspace:*",
"@memoh/memory": "workspace:*",
"@memoh/shared": "workspace:*",
"@memoh/platform": "workspace:*",
"@memoh/platform-telegram": "workspace:*",
"drizzle-orm": "^0.45.1",
"elysia": "latest",
"node-cron": "^4.2.1",
+11 -3
View File
@@ -7,6 +7,7 @@ import {
DeletePlatformModel,
UpdatePlatformConfigModel,
SetPlatformActiveModel,
getPlatformConfigSchema,
} from './model'
import {
getPlatforms,
@@ -34,7 +35,6 @@ export const platformModule = new Elysia({
await activePlatform({
id: platform.id,
name: platform.name,
endpoint: platform.endpoint,
config: platform.config as Record<string, unknown>,
active: platform.active,
})
@@ -140,13 +140,21 @@ export const platformModule = new Elysia({
try {
const { id } = params
const { config } = body as { config: Record<string, unknown> }
const updatedPlatform = await updatePlatformConfig(id, config)
if (!updatedPlatform) {
// Get the platform to validate config against its schema
const platform = await getPlatformById(id)
if (!platform) {
return {
success: false,
error: 'Platform not found',
}
}
// Validate config against platform-specific schema
const configSchema = getPlatformConfigSchema(platform.name)
const validatedConfig = configSchema.parse(config) as Record<string, unknown>
const updatedPlatform = await updatePlatformConfig(id, validatedConfig)
return {
success: true,
data: updatedPlatform,
+52 -1
View File
@@ -1,10 +1,59 @@
import { z } from 'zod'
// Platform-specific config schemas
export const TelegramConfigSchema = z.object({
botToken: z.string().min(1, 'Bot token is required'),
})
// Registry of platform config schemas
// When adding a new platform, add its config schema here
export const platformConfigSchemas: Record<string, z.ZodSchema> = {
telegram: TelegramConfigSchema,
// Add more platforms here as they are implemented
// discord: DiscordConfigSchema,
// slack: SlackConfigSchema,
}
// Helper function to get config schema for a platform
export const getPlatformConfigSchema = (platformName: string): z.ZodSchema => {
const schema = platformConfigSchemas[platformName]
if (!schema) {
throw new Error(`Unknown platform: ${platformName}. Supported platforms: ${Object.keys(platformConfigSchemas).join(', ')}`)
}
return schema
}
// Base platform schema with dynamic config validation
const PlatformSchema = z.object({
name: z.string().min(1, 'Platform name is required'),
endpoint: z.string().min(1, 'Endpoint is required'),
config: z.record(z.string(), z.unknown()),
active: z.boolean().optional().default(true),
}).superRefine((data, ctx) => {
// Validate that the platform name is supported
if (!platformConfigSchemas[data.name]) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Unknown platform: ${data.name}. Supported platforms: ${Object.keys(platformConfigSchemas).join(', ')}`,
path: ['name'],
})
return
}
// Validate the config against the platform-specific schema
try {
const configSchema = getPlatformConfigSchema(data.name)
configSchema.parse(data.config)
} catch (error) {
if (error instanceof z.ZodError) {
error.issues.forEach((issue: z.ZodIssue) => {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: issue.message,
path: ['config', ...issue.path],
})
})
}
}
})
export type PlatformInput = z.infer<typeof PlatformSchema>
@@ -32,6 +81,8 @@ export const DeletePlatformModel = {
}),
}
// For updating config, we need to know the platform name to validate
// This will be used with additional validation in the route handler
export const UpdatePlatformConfigModel = {
params: z.object({
id: z.string(),
+26 -30
View File
@@ -3,15 +3,12 @@ import { platform } from '@memoh/db/schema'
import { Platform } from '@memoh/shared'
import { eq, sql, desc, asc } from 'drizzle-orm'
import { calculateOffset, createPaginatedResult, type PaginatedResult } from '../../utils/pagination'
import path from 'node:path'
import { BasePlatform } from '@memoh/platform'
import { TelegramPlatform } from '@memoh/platform-telegram'
/**
* 平台列表返回类型
*/
type PlatformListItem = {
id: string
name: string
endpoint: string
config: Record<string, unknown>
active: boolean
createdAt: Date
@@ -72,7 +69,6 @@ export const createPlatform = async (data: Omit<Platform, 'id'>) => {
.insert(platform)
.values({
name: data.name,
endpoint: data.endpoint,
config: data.config,
active: data.active ?? true,
})
@@ -81,7 +77,6 @@ export const createPlatform = async (data: Omit<Platform, 'id'>) => {
await activePlatform({
id: newPlatform.id,
name: newPlatform.name,
endpoint: newPlatform.endpoint,
config: newPlatform.config as Record<string, unknown>,
active: newPlatform.active,
})
@@ -92,7 +87,6 @@ export const createPlatform = async (data: Omit<Platform, 'id'>) => {
export const updatePlatform = async (id: string, data: Partial<Omit<Platform, 'id'>>) => {
const updateData: {
name?: string
endpoint?: string
config?: Record<string, unknown>
active?: boolean
updatedAt: Date
@@ -101,7 +95,6 @@ export const updatePlatform = async (id: string, data: Partial<Omit<Platform, 'i
}
if (data.name !== undefined) updateData.name = data.name
if (data.endpoint !== undefined) updateData.endpoint = data.endpoint
if (data.config !== undefined) updateData.config = data.config
if (data.active !== undefined) updateData.active = data.active
@@ -135,23 +128,29 @@ export const updatePlatformConfig = async (id: string, config: Record<string, un
// active
export const platformConstructors: Record<string, typeof BasePlatform> = {
telegram: TelegramPlatform,
}
export const platforms = new Map<string, BasePlatform>()
export const activePlatform = async (platform: Platform) => {
await fetch(path.join(platform.endpoint, '/start'), {
method: 'POST',
body: JSON.stringify(platform.config),
headers: {
'Content-Type': 'application/json',
},
})
const Constructor = platformConstructors[platform.name]
if (!Constructor) {
throw new Error('Platform constructor not found')
}
const platformInstance = new Constructor()
await platformInstance.start(platform.config)
platforms.set(platform.name, platformInstance)
}
export const inactivePlatform = async (platform: Platform) => {
await fetch(path.join(platform.endpoint, '/stop'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
})
const platformInstance = platforms.get(platform.name)
if (!platformInstance) {
throw new Error('Platform not found')
}
await platformInstance.stop()
platforms.delete(platform.name)
}
export const setActivePlatform = async (id: string, active: boolean) => {
@@ -162,7 +161,6 @@ export const setActivePlatform = async (id: string, active: boolean) => {
const platformData: Platform = {
id: currentPlatform.id,
name: currentPlatform.name,
endpoint: currentPlatform.endpoint,
config: currentPlatform.config as Record<string, unknown>,
active: active,
}
@@ -187,11 +185,9 @@ export const sendMessageToPlatform = async (name: string, options: {
if (!currentPlatform) {
throw new Error('Platform not found')
}
await fetch(path.join(currentPlatform.endpoint, '/send'), {
method: 'POST',
body: JSON.stringify(options),
headers: {
'Content-Type': 'application/json',
},
})
const platformInstance = platforms.get(currentPlatform.name)
if (!platformInstance) {
throw new Error('Platform not found')
}
await platformInstance.send(options)
}