mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-07-07 06:42:30 +00:00
* ⚡️ refactor: Migrate @librechat/api build to tsdown Replace Rollup with tsdown (rolldown + oxc isolated-declarations) for the @librechat/api package build, mirroring the merged data-schemas migration. - Add tsdown.config.mjs (cjs output, oxc dts, externalize all bare deps, bundle first-party `~/` + relative imports) - Annotate exports for isolatedDeclarations (codefix-driven). Collapse the tokens.ts model->token maps to Record<string, Record<string, number>> and switch validation.ts's runtime `files` field from z.any() to z.unknown() so no explicit `any` is introduced - Repoint package.json main/types/exports to tsdown's .cjs/.d.cts output - Add src/telemetry.ts entry shim so the two index.ts entries don't collide in oxc's flat dts output (stable dist/telemetry.{cjs,d.cts}) - Delete rollup.config.js Build time ~36s -> ~0.5s. No runtime behavior change: 5712 unit tests pass, both entries load via require(), legacy /api consumes them unchanged. * 👷 ci: Hash packages/api/tsdown.config.mjs in build-api cache keys The build-api cache keys hashed `packages/api/server-rollup.config.js`, which never existed (api used `rollup.config.js`, now removed) — a copy-paste artifact from the data-provider key that matched no file. Replace it with the new `packages/api/tsdown.config.mjs` so edits to the build config (entry, format, externals) bust the api build cache, matching the data-schemas key.
157 lines
4.4 KiB
TypeScript
157 lines
4.4 KiB
TypeScript
import { createMethods } from '@librechat/data-schemas';
|
|
import { ResourceType, PermissionBits, hasPermissions } from 'librechat-data-provider';
|
|
import type {
|
|
AgentApiKeyListItem,
|
|
AgentApiKeyCreateResult,
|
|
AllMethods,
|
|
IUser,
|
|
} from '@librechat/data-schemas';
|
|
import type { Types } from 'mongoose';
|
|
|
|
export interface ApiKeyServiceDependencies {
|
|
validateAgentApiKey: AllMethods['validateAgentApiKey'];
|
|
createAgentApiKey: AllMethods['createAgentApiKey'];
|
|
listAgentApiKeys: AllMethods['listAgentApiKeys'];
|
|
deleteAgentApiKey: AllMethods['deleteAgentApiKey'];
|
|
getAgentApiKeyById: AllMethods['getAgentApiKeyById'];
|
|
findUser: (query: { _id: string | Types.ObjectId }) => Promise<IUser | null>;
|
|
}
|
|
|
|
export interface RemoteAgentAccessResult {
|
|
hasAccess: boolean;
|
|
permissions: number;
|
|
agent: { _id: Types.ObjectId; [key: string]: unknown } | null;
|
|
}
|
|
|
|
export class AgentApiKeyService {
|
|
private deps: ApiKeyServiceDependencies;
|
|
|
|
constructor(deps: ApiKeyServiceDependencies) {
|
|
this.deps = deps;
|
|
}
|
|
|
|
async validateApiKey(apiKey: string): Promise<{
|
|
userId: Types.ObjectId;
|
|
keyId: Types.ObjectId;
|
|
} | null> {
|
|
return this.deps.validateAgentApiKey(apiKey);
|
|
}
|
|
|
|
async createApiKey(params: {
|
|
userId: string | Types.ObjectId;
|
|
name: string;
|
|
expiresAt?: Date | null;
|
|
}): Promise<AgentApiKeyCreateResult> {
|
|
return this.deps.createAgentApiKey(params);
|
|
}
|
|
|
|
async listApiKeys(userId: string | Types.ObjectId): Promise<AgentApiKeyListItem[]> {
|
|
return this.deps.listAgentApiKeys(userId);
|
|
}
|
|
|
|
async deleteApiKey(
|
|
keyId: string | Types.ObjectId,
|
|
userId: string | Types.ObjectId,
|
|
): Promise<boolean> {
|
|
return this.deps.deleteAgentApiKey(keyId, userId);
|
|
}
|
|
|
|
async getApiKeyById(
|
|
keyId: string | Types.ObjectId,
|
|
userId: string | Types.ObjectId,
|
|
): Promise<AgentApiKeyListItem | null> {
|
|
return this.deps.getAgentApiKeyById(keyId, userId);
|
|
}
|
|
|
|
async getUserFromApiKey(apiKey: string): Promise<IUser | null> {
|
|
const keyValidation = await this.validateApiKey(apiKey);
|
|
if (!keyValidation) {
|
|
return null;
|
|
}
|
|
|
|
return this.deps.findUser({ _id: keyValidation.userId });
|
|
}
|
|
}
|
|
|
|
export function createApiKeyServiceDependencies(
|
|
mongoose: typeof import('mongoose'),
|
|
): ApiKeyServiceDependencies {
|
|
const methods = createMethods(mongoose);
|
|
return {
|
|
validateAgentApiKey: methods.validateAgentApiKey,
|
|
createAgentApiKey: methods.createAgentApiKey,
|
|
listAgentApiKeys: methods.listAgentApiKeys,
|
|
deleteAgentApiKey: methods.deleteAgentApiKey,
|
|
getAgentApiKeyById: methods.getAgentApiKeyById,
|
|
findUser: methods.findUser,
|
|
};
|
|
}
|
|
|
|
export interface GetRemoteAgentPermissionsDeps {
|
|
getEffectivePermissions: (params: {
|
|
userId: string;
|
|
role?: string;
|
|
resourceType: ResourceType;
|
|
resourceId: string | Types.ObjectId;
|
|
}) => Promise<number>;
|
|
}
|
|
|
|
/** AGENT owners automatically have full REMOTE_AGENT permissions */
|
|
export async function getRemoteAgentPermissions(
|
|
deps: GetRemoteAgentPermissionsDeps,
|
|
userId: string,
|
|
role: string | undefined,
|
|
resourceId: string | Types.ObjectId,
|
|
): Promise<number> {
|
|
const agentPerms = await deps.getEffectivePermissions({
|
|
userId,
|
|
role,
|
|
resourceType: ResourceType.AGENT,
|
|
resourceId,
|
|
});
|
|
|
|
if (hasPermissions(agentPerms, PermissionBits.SHARE)) {
|
|
return PermissionBits.VIEW | PermissionBits.EDIT | PermissionBits.DELETE | PermissionBits.SHARE;
|
|
}
|
|
|
|
return deps.getEffectivePermissions({
|
|
userId,
|
|
role,
|
|
resourceType: ResourceType.REMOTE_AGENT,
|
|
resourceId,
|
|
});
|
|
}
|
|
|
|
export async function checkRemoteAgentAccess(params: {
|
|
userId: string;
|
|
role?: string;
|
|
agentId: string;
|
|
getAgent: (query: {
|
|
id: string;
|
|
}) => Promise<{ _id: Types.ObjectId; [key: string]: unknown } | null>;
|
|
getEffectivePermissions: (params: {
|
|
userId: string;
|
|
role?: string;
|
|
resourceType: ResourceType;
|
|
resourceId: string | Types.ObjectId;
|
|
}) => Promise<number>;
|
|
}): Promise<RemoteAgentAccessResult> {
|
|
const { userId, role, agentId, getAgent, getEffectivePermissions } = params;
|
|
|
|
const agent = await getAgent({ id: agentId });
|
|
|
|
if (!agent) {
|
|
return { hasAccess: false, permissions: 0, agent: null };
|
|
}
|
|
|
|
const permissions = await getRemoteAgentPermissions(
|
|
{ getEffectivePermissions },
|
|
userId,
|
|
role,
|
|
agent._id,
|
|
);
|
|
|
|
const hasAccess = hasPermissions(permissions, PermissionBits.VIEW);
|
|
|
|
return { hasAccess, permissions, agent };
|
|
}
|