main
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@zbrain/shared",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./types": "./src/types.ts",
|
||||
"./schemas": "./src/schemas.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "^3.23.8"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './types.js';
|
||||
export * from './schemas.js';
|
||||
@@ -0,0 +1,63 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const UserRoleSchema = z.enum(['owner', 'admin', 'editor', 'viewer']);
|
||||
|
||||
export const LoginRequestSchema = z.object({
|
||||
email: z.string().email(),
|
||||
password: z.string().min(8).max(256),
|
||||
totpCode: z.string().regex(/^\d{6}$/).optional(),
|
||||
});
|
||||
|
||||
export const CreateBrainSchema = z.object({
|
||||
name: z.string().regex(/^[a-z0-9_]{1,32}$/, 'Только [a-z0-9_], до 32 символов'),
|
||||
displayName: z.string().min(1).max(128),
|
||||
description: z.string().max(1024).optional(),
|
||||
});
|
||||
|
||||
export const MCPScopeSchema = z.string().regex(
|
||||
/^mcp:(read|write|admin):[a-z0-9_*]{1,32}$/,
|
||||
'Формат: mcp:<read|write|admin>:<brain-name>'
|
||||
) as z.ZodType<`mcp:${'read' | 'write' | 'admin'}:${string}`>;
|
||||
|
||||
export const CreateTokenSchema = z.object({
|
||||
name: z.string().min(1).max(128),
|
||||
scopes: z.array(MCPScopeSchema).min(1).max(20),
|
||||
ipAllowlist: z.array(z.string()).max(50).optional(),
|
||||
expiresInDays: z.number().int().min(1).max(3650).nullable().optional(),
|
||||
});
|
||||
|
||||
export const SourceConfigGitSchema = z.object({
|
||||
type: z.literal('git'),
|
||||
url: z.string().url(),
|
||||
branch: z.string().default('main'),
|
||||
subpath: z.string().optional(),
|
||||
localPath: z.string(),
|
||||
});
|
||||
|
||||
export const SourceConfigLocalDirSchema = z.object({
|
||||
type: z.literal('local_dir'),
|
||||
path: z.string(),
|
||||
});
|
||||
|
||||
export const SourceConfigManualSchema = z.object({
|
||||
type: z.literal('manual'),
|
||||
});
|
||||
|
||||
export const SourceConfigSchema = z.discriminatedUnion('type', [
|
||||
SourceConfigGitSchema,
|
||||
SourceConfigLocalDirSchema,
|
||||
SourceConfigManualSchema,
|
||||
]);
|
||||
|
||||
export const CreateSourceSchema = z.object({
|
||||
type: z.enum(['git', 'local_dir', 'manual']),
|
||||
config: SourceConfigSchema,
|
||||
schedule: z.string().nullable().optional(), // cron expression
|
||||
});
|
||||
|
||||
export const CreateProjectSchema = z.object({
|
||||
name: z.string().min(1).max(128),
|
||||
slug: z.string().regex(/^[a-z0-9-]{1,64}$/, 'lowercase, цифры, дефисы'),
|
||||
repoUrl: z.string().url().nullable().optional(),
|
||||
description: z.string().max(1024).nullable().optional(),
|
||||
});
|
||||
@@ -0,0 +1,145 @@
|
||||
// Shared types - используются и в API, и в Web
|
||||
// Источник правды для контракта между frontend и backend
|
||||
|
||||
export type UserRole = 'owner' | 'admin' | 'editor' | 'viewer';
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
displayName: string | null;
|
||||
role: UserRole;
|
||||
oauthProvider: 'yandex' | 'github' | null;
|
||||
totpEnabled: boolean;
|
||||
createdAt: string;
|
||||
lastLoginAt: string | null;
|
||||
}
|
||||
|
||||
export interface Brain {
|
||||
id: string;
|
||||
name: string;
|
||||
displayName: string;
|
||||
description: string | null;
|
||||
postgresDatabase: string;
|
||||
mcpInternalPort: number;
|
||||
ownerUserId: string;
|
||||
createdAt: string;
|
||||
|
||||
// Computed stats
|
||||
stats?: BrainStats;
|
||||
}
|
||||
|
||||
export interface BrainStats {
|
||||
pageCount: number;
|
||||
chunkCount: number;
|
||||
embeddedCount: number;
|
||||
linkCount: number;
|
||||
diskSizeMB: number;
|
||||
lastSyncAt: string | null;
|
||||
healthStatus: 'healthy' | 'warning' | 'critical';
|
||||
embeddingCoverage: number; // 0..1
|
||||
}
|
||||
|
||||
export type SourceType = 'git' | 'local_dir' | 'manual';
|
||||
|
||||
export interface Source {
|
||||
id: string;
|
||||
brainId: string;
|
||||
type: SourceType;
|
||||
config: SourceConfig;
|
||||
schedule: string | null;
|
||||
lastSyncAt: string | null;
|
||||
lastSyncStatus: SyncStatus | null;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export type SourceConfig =
|
||||
| { type: 'git'; url: string; branch: string; subpath?: string; localPath: string }
|
||||
| { type: 'local_dir'; path: string }
|
||||
| { type: 'manual' };
|
||||
|
||||
export type SyncStatus = 'idle' | 'running' | 'success' | 'failed';
|
||||
|
||||
export type MCPScope = `mcp:${'read' | 'write' | 'admin'}:${string}`;
|
||||
|
||||
export interface AccessToken {
|
||||
id: string;
|
||||
name: string;
|
||||
prefix: string;
|
||||
createdByUserId: string;
|
||||
scopes: MCPScope[];
|
||||
ipAllowlist: string[] | null;
|
||||
createdAt: string;
|
||||
expiresAt: string | null;
|
||||
lastUsedAt: string | null;
|
||||
lastUsedIp: string | null;
|
||||
revokedAt: string | null;
|
||||
}
|
||||
|
||||
export interface CreateTokenRequest {
|
||||
name: string;
|
||||
scopes: MCPScope[];
|
||||
ipAllowlist?: string[];
|
||||
expiresInDays?: number; // null = no expiration
|
||||
}
|
||||
|
||||
export interface CreateTokenResponse {
|
||||
token: AccessToken;
|
||||
plaintext: string; // показывается ОДИН РАЗ
|
||||
}
|
||||
|
||||
export interface Project {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
repoUrl: string | null;
|
||||
description: string | null;
|
||||
ownerUserId: string;
|
||||
createdAt: string;
|
||||
connections: ProjectConnection[];
|
||||
}
|
||||
|
||||
export interface ProjectConnection {
|
||||
brainId: string;
|
||||
brainName: string;
|
||||
brainDisplayName: string;
|
||||
tokenId: string;
|
||||
tokenName: string;
|
||||
scope: 'read' | 'write';
|
||||
}
|
||||
|
||||
export interface ConnectSnippet {
|
||||
client: 'claude-code' | 'cursor' | 'cli';
|
||||
language: string;
|
||||
filename: string | null;
|
||||
content: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface AuditEntry {
|
||||
id: number;
|
||||
actorType: 'user' | 'token';
|
||||
actorId: string;
|
||||
actorName: string;
|
||||
action: string;
|
||||
resourceType: string | null;
|
||||
resourceId: string | null;
|
||||
payload: Record<string, unknown> | null;
|
||||
ip: string | null;
|
||||
userAgent: string | null;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
// API responses
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
items: T[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export interface ApiError {
|
||||
error: string;
|
||||
code: string;
|
||||
details?: Record<string, unknown>;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"lib": ["ES2022"],
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"verbatimModuleSyntax": false,
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user