feat: Add cursor pagination utilities and refine user/group/role types in @librechat/data-schemas (#9218)

* feat: Add pagination interfaces and update user and group types for better data handling

* fix: Update data-schemas version to 0.0.19
This commit is contained in:
Marco Beretta 2025-08-23 06:18:31 +02:00 committed by GitHub
parent 0e00f357a6
commit ac608ded46
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 108 additions and 20 deletions

2
package-lock.json generated
View file

@ -51876,7 +51876,7 @@
}, },
"packages/data-schemas": { "packages/data-schemas": {
"name": "@librechat/data-schemas", "name": "@librechat/data-schemas",
"version": "0.0.18", "version": "0.0.19",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-alias": "^5.1.0",

View file

@ -1,6 +1,6 @@
{ {
"name": "@librechat/data-schemas", "name": "@librechat/data-schemas",
"version": "0.0.18", "version": "0.0.19",
"description": "Mongoose schemas and models for LibreChat", "description": "Mongoose schemas and models for LibreChat",
"type": "module", "type": "module",
"main": "dist/index.cjs", "main": "dist/index.cjs",

View file

@ -1 +1,2 @@
export * from './enum'; export * from './enum';
export * from './pagination';

View file

@ -0,0 +1,17 @@
export interface CursorPaginationParams {
limit?: number;
cursor?: string;
sortBy?: string;
sortOrder?: 'asc' | 'desc';
}
export interface CursorPaginationResponse<T> {
data: T[];
pagination: {
hasNextPage: boolean;
hasPreviousPage: boolean;
nextCursor?: string;
previousCursor?: string;
totalCount?: number;
};
}

View file

@ -1,5 +1,5 @@
import mongoose, { FilterQuery } from 'mongoose'; import mongoose, { FilterQuery } from 'mongoose';
import type { IUser, BalanceConfig, UserCreateData, UserUpdateResult } from '~/types'; import type { IUser, BalanceConfig, CreateUserRequest, UserDeleteResult } from '~/types';
import { signPayload } from '~/crypto'; import { signPayload } from '~/crypto';
/** Factory function that takes mongoose instance and returns the methods */ /** Factory function that takes mongoose instance and returns the methods */
@ -31,7 +31,7 @@ export function createUserMethods(mongoose: typeof import('mongoose')) {
* Creates a new user, optionally with a TTL of 1 week. * Creates a new user, optionally with a TTL of 1 week.
*/ */
async function createUser( async function createUser(
data: UserCreateData, data: CreateUserRequest,
balanceConfig?: BalanceConfig, balanceConfig?: BalanceConfig,
disableTTL: boolean = true, disableTTL: boolean = true,
returnUser: boolean = false, returnUser: boolean = false,
@ -123,7 +123,7 @@ export function createUserMethods(mongoose: typeof import('mongoose')) {
/** /**
* Delete a user by their unique ID. * Delete a user by their unique ID.
*/ */
async function deleteUserById(userId: string): Promise<UserUpdateResult> { async function deleteUserById(userId: string): Promise<UserDeleteResult> {
try { try {
const User = mongoose.models.User; const User = mongoose.models.User;
const result = await User.deleteOne({ _id: userId }); const result = await User.deleteOne({ _id: userId });

View file

@ -24,6 +24,7 @@ const groupSchema = new Schema<IGroup>(
memberIds: [ memberIds: [
{ {
type: String, type: String,
required: false,
}, },
], ],
source: { source: {

View file

@ -1,22 +1,44 @@
import type { Document, Types } from 'mongoose'; import type { Document, Types } from 'mongoose';
import { CursorPaginationParams } from '~/common';
export interface IGroup extends Document { export interface IGroup extends Document {
_id: Types.ObjectId; _id: Types.ObjectId;
/** The name of the group */
name: string; name: string;
/** Optional description of the group */
description?: string; description?: string;
/** Optional email address for the group */
email?: string; email?: string;
/** Optional avatar URL for the group */
avatar?: string; avatar?: string;
/** Array of member IDs (stores idOnTheSource values, not ObjectIds) */ /** Array of member IDs (stores idOnTheSource values, not ObjectIds) */
memberIds: string[]; memberIds?: string[];
/** The source of the group ('local' or 'entra') */
source: 'local' | 'entra'; source: 'local' | 'entra';
/** External ID (e.g., Entra ID) - required for non-local sources */ /** External ID (e.g., Entra ID) - required for non-local sources */
idOnTheSource?: string; idOnTheSource?: string;
/** Timestamps */
createdAt?: Date; createdAt?: Date;
updatedAt?: Date; updatedAt?: Date;
} }
export interface CreateGroupRequest {
name: string;
description?: string;
email?: string;
avatar?: string;
memberIds?: string[];
source: 'local' | 'entra';
idOnTheSource?: string;
}
export interface UpdateGroupRequest {
name?: string;
description?: string;
email?: string;
avatar?: string;
memberIds?: string[];
source?: 'local' | 'entra' | 'ldap';
idOnTheSource?: string;
}
export interface GroupFilterOptions extends CursorPaginationParams {
// Includes email, name and description
search?: string;
source?: 'local' | 'entra' | 'ldap';
hasMember?: string;
}

View file

@ -1,5 +1,6 @@
import { Document } from 'mongoose';
import { PermissionTypes, Permissions } from 'librechat-data-provider'; import { PermissionTypes, Permissions } from 'librechat-data-provider';
import type { Document } from 'mongoose';
import { CursorPaginationParams } from '~/common';
export interface IRole extends Document { export interface IRole extends Document {
name: string; name: string;
@ -48,3 +49,25 @@ export interface IRole extends Document {
}; };
}; };
} }
export type RolePermissions = IRole['permissions'];
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
export type RolePermissionsInput = DeepPartial<RolePermissions>;
export interface CreateRoleRequest {
name: string;
permissions: RolePermissionsInput;
}
export interface UpdateRoleRequest {
name?: string;
permissions?: RolePermissionsInput;
}
export interface RoleFilterOptions extends CursorPaginationParams {
// Includes role name
search?: string;
hasPermission?: string;
}

View file

@ -1,4 +1,5 @@
import { Document, Types } from 'mongoose'; import type { Document, Types } from 'mongoose';
import { CursorPaginationParams } from '~/common';
export interface IUser extends Document { export interface IUser extends Document {
name?: string; name?: string;
@ -48,18 +49,39 @@ export interface BalanceConfig {
refillAmount?: number; refillAmount?: number;
} }
export interface UserCreateData extends Partial<IUser> { export interface CreateUserRequest extends Partial<IUser> {
email: string; email: string;
} }
export interface UserUpdateResult { export interface UpdateUserRequest {
name?: string;
username?: string;
email?: string;
role?: string;
emailVerified?: boolean;
avatar?: string;
plugins?: string[];
twoFactorEnabled?: boolean;
termsAccepted?: boolean;
personalization?: {
memories?: boolean;
};
}
export interface UserDeleteResult {
deletedCount: number; deletedCount: number;
message: string; message: string;
} }
export interface UserSearchCriteria { export interface UserFilterOptions extends CursorPaginationParams {
email?: string; _id?: Types.ObjectId | string;
username?: string; // Includes email, username and name
search?: string;
role?: string;
emailVerified?: boolean;
provider?: string;
twoFactorEnabled?: boolean;
// External IDs
googleId?: string; googleId?: string;
facebookId?: string; facebookId?: string;
openidId?: string; openidId?: string;
@ -68,7 +90,9 @@ export interface UserSearchCriteria {
githubId?: string; githubId?: string;
discordId?: string; discordId?: string;
appleId?: string; appleId?: string;
_id?: Types.ObjectId | string; // Date filters
createdAfter?: string;
createdBefore?: string;
} }
export interface UserQueryOptions { export interface UserQueryOptions {