mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 06:00:56 +02:00
🗄️ refactor: Resource Migration Scripts for DocumentDB Compatibility (#9249)
* refactor: Resource Migration Scripts for DocumentDB compatibility * fix: Correct type annotation for `db` parameter in ensureCollectionExists function
This commit is contained in:
parent
1915d7b195
commit
ac641e7cba
5 changed files with 168 additions and 28 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
const mongoose = require('mongoose');
|
||||||
const { logger } = require('@librechat/data-schemas');
|
const { logger } = require('@librechat/data-schemas');
|
||||||
const {
|
const {
|
||||||
logAgentMigrationWarning,
|
logAgentMigrationWarning,
|
||||||
|
@ -16,7 +17,8 @@ const { findRoleByIdentifier } = require('~/models');
|
||||||
async function checkMigrations() {
|
async function checkMigrations() {
|
||||||
try {
|
try {
|
||||||
const agentMigrationResult = await checkAgentPermissionsMigration({
|
const agentMigrationResult = await checkAgentPermissionsMigration({
|
||||||
db: {
|
mongoose,
|
||||||
|
methods: {
|
||||||
findRoleByIdentifier,
|
findRoleByIdentifier,
|
||||||
getProjectByName,
|
getProjectByName,
|
||||||
},
|
},
|
||||||
|
@ -28,7 +30,8 @@ async function checkMigrations() {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const promptMigrationResult = await checkPromptPermissionsMigration({
|
const promptMigrationResult = await checkPromptPermissionsMigration({
|
||||||
db: {
|
mongoose,
|
||||||
|
methods: {
|
||||||
findRoleByIdentifier,
|
findRoleByIdentifier,
|
||||||
getProjectByName,
|
getProjectByName,
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,6 +16,38 @@ async function migrateAgentPermissionsEnhanced({ dryRun = true, batchSize = 100
|
||||||
|
|
||||||
logger.info('Starting Enhanced Agent Permissions Migration', { dryRun, batchSize });
|
logger.info('Starting Enhanced Agent Permissions Migration', { dryRun, batchSize });
|
||||||
|
|
||||||
|
/** Ensurse `aclentries` collection exists for DocumentDB compatibility
|
||||||
|
* @param {import('mongoose').mongo.Db} db
|
||||||
|
* @param {string} collectionName
|
||||||
|
*/
|
||||||
|
async function ensureCollectionExists(db, collectionName) {
|
||||||
|
try {
|
||||||
|
const collections = await db.listCollections({ name: collectionName }).toArray();
|
||||||
|
if (collections.length === 0) {
|
||||||
|
await db.createCollection(collectionName);
|
||||||
|
logger.info(`Created collection: ${collectionName}`);
|
||||||
|
} else {
|
||||||
|
logger.info(`Collection already exists: ${collectionName}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`'Failed to check/create "${collectionName}" collection:`, error);
|
||||||
|
// If listCollections fails, try alternative approach
|
||||||
|
try {
|
||||||
|
// Try to access the collection directly - this will create it in MongoDB if it doesn't exist
|
||||||
|
await db.collection(collectionName).findOne({}, { projection: { _id: 1 } });
|
||||||
|
} catch (createError) {
|
||||||
|
logger.error(`Could not ensure collection ${collectionName} exists:`, createError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
/** @type {import('mongoose').mongo.Db | undefined} */
|
||||||
|
const db = mongoose.connection.db;
|
||||||
|
if (db) {
|
||||||
|
await ensureCollectionExists(db, 'aclentries');
|
||||||
|
}
|
||||||
|
|
||||||
// Verify required roles exist
|
// Verify required roles exist
|
||||||
const ownerRole = await findRoleByIdentifier(AccessRoleIds.AGENT_OWNER);
|
const ownerRole = await findRoleByIdentifier(AccessRoleIds.AGENT_OWNER);
|
||||||
const viewerRole = await findRoleByIdentifier(AccessRoleIds.AGENT_VIEWER);
|
const viewerRole = await findRoleByIdentifier(AccessRoleIds.AGENT_VIEWER);
|
||||||
|
@ -100,12 +132,19 @@ async function migrateAgentPermissionsEnhanced({ dryRun = true, batchSize = 100
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info('Agent categorization:', {
|
logger.info(
|
||||||
globalEditAccess: categories.globalEditAccess.length,
|
'Agent categorization:\n' +
|
||||||
globalViewAccess: categories.globalViewAccess.length,
|
JSON.stringify(
|
||||||
privateAgents: categories.privateAgents.length,
|
{
|
||||||
total: agentsToMigrate.length,
|
globalEditAccess: categories.globalEditAccess.length,
|
||||||
});
|
globalViewAccess: categories.globalViewAccess.length,
|
||||||
|
privateAgents: categories.privateAgents.length,
|
||||||
|
total: agentsToMigrate.length,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
if (dryRun) {
|
if (dryRun) {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -16,6 +16,38 @@ async function migrateToPromptGroupPermissions({ dryRun = true, batchSize = 100
|
||||||
|
|
||||||
logger.info('Starting PromptGroup Permissions Migration', { dryRun, batchSize });
|
logger.info('Starting PromptGroup Permissions Migration', { dryRun, batchSize });
|
||||||
|
|
||||||
|
/** Ensurse `aclentries` collection exists for DocumentDB compatibility
|
||||||
|
* @param {import('mongoose').mongo.Db} db
|
||||||
|
* @param {string} collectionName
|
||||||
|
*/
|
||||||
|
async function ensureCollectionExists(db, collectionName) {
|
||||||
|
try {
|
||||||
|
const collections = await db.listCollections({ name: collectionName }).toArray();
|
||||||
|
if (collections.length === 0) {
|
||||||
|
await db.createCollection(collectionName);
|
||||||
|
logger.info(`Created collection: ${collectionName}`);
|
||||||
|
} else {
|
||||||
|
logger.info(`Collection already exists: ${collectionName}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`'Failed to check/create "${collectionName}" collection:`, error);
|
||||||
|
// If listCollections fails, try alternative approach
|
||||||
|
try {
|
||||||
|
// Try to access the collection directly - this will create it in MongoDB if it doesn't exist
|
||||||
|
await db.collection(collectionName).findOne({}, { projection: { _id: 1 } });
|
||||||
|
} catch (createError) {
|
||||||
|
logger.error(`Could not ensure collection ${collectionName} exists:`, createError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
/** @type {import('mongoose').mongo.Db | undefined} */
|
||||||
|
const db = mongoose.connection.db;
|
||||||
|
if (db) {
|
||||||
|
await ensureCollectionExists(db, 'aclentries');
|
||||||
|
}
|
||||||
|
|
||||||
// Verify required roles exist
|
// Verify required roles exist
|
||||||
const ownerRole = await findRoleByIdentifier(AccessRoleIds.PROMPTGROUP_OWNER);
|
const ownerRole = await findRoleByIdentifier(AccessRoleIds.PROMPTGROUP_OWNER);
|
||||||
const viewerRole = await findRoleByIdentifier(AccessRoleIds.PROMPTGROUP_VIEWER);
|
const viewerRole = await findRoleByIdentifier(AccessRoleIds.PROMPTGROUP_VIEWER);
|
||||||
|
@ -91,11 +123,18 @@ async function migrateToPromptGroupPermissions({ dryRun = true, batchSize = 100
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info('PromptGroup categorization:', {
|
logger.info(
|
||||||
globalViewAccess: categories.globalViewAccess.length,
|
'PromptGroup categorization:\n' +
|
||||||
privateGroups: categories.privateGroups.length,
|
JSON.stringify(
|
||||||
total: promptGroupsToMigrate.length,
|
{
|
||||||
});
|
globalViewAccess: categories.globalViewAccess.length,
|
||||||
|
privateGroups: categories.privateGroups.length,
|
||||||
|
total: promptGroupsToMigrate.length,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
if (dryRun) {
|
if (dryRun) {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { logger } from '@librechat/data-schemas';
|
import { logger } from '@librechat/data-schemas';
|
||||||
import { AccessRoleIds, ResourceType, PrincipalType, Constants } from 'librechat-data-provider';
|
import { AccessRoleIds, ResourceType, PrincipalType, Constants } from 'librechat-data-provider';
|
||||||
import type { AccessRoleMethods, IAgent } from '@librechat/data-schemas';
|
import type { AccessRoleMethods, IAgent } from '@librechat/data-schemas';
|
||||||
import type { Model } from 'mongoose';
|
import type { Model, Mongoose, mongo } from 'mongoose';
|
||||||
|
|
||||||
const { GLOBAL_PROJECT_NAME } = Constants;
|
const { GLOBAL_PROJECT_NAME } = Constants;
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@ export interface MigrationCheckDbMethods {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MigrationCheckParams {
|
export interface MigrationCheckParams {
|
||||||
db: MigrationCheckDbMethods;
|
mongoose: Mongoose;
|
||||||
|
methods: MigrationCheckDbMethods;
|
||||||
AgentModel: Model<IAgent>;
|
AgentModel: Model<IAgent>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,16 +47,44 @@ export interface MigrationCheckResult {
|
||||||
* This performs a dry-run check similar to the migration script
|
* This performs a dry-run check similar to the migration script
|
||||||
*/
|
*/
|
||||||
export async function checkAgentPermissionsMigration({
|
export async function checkAgentPermissionsMigration({
|
||||||
db,
|
methods,
|
||||||
|
mongoose,
|
||||||
AgentModel,
|
AgentModel,
|
||||||
}: MigrationCheckParams): Promise<MigrationCheckResult> {
|
}: MigrationCheckParams): Promise<MigrationCheckResult> {
|
||||||
logger.debug('Checking if agent permissions migration is needed');
|
logger.debug('Checking if agent permissions migration is needed');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
/** Ensurse `aclentries` collection exists for DocumentDB compatibility */
|
||||||
|
async function ensureCollectionExists(db: mongo.Db, collectionName: string) {
|
||||||
|
try {
|
||||||
|
const collections = await db.listCollections({ name: collectionName }).toArray();
|
||||||
|
if (collections.length === 0) {
|
||||||
|
await db.createCollection(collectionName);
|
||||||
|
logger.info(`Created collection: ${collectionName}`);
|
||||||
|
} else {
|
||||||
|
logger.debug(`Collection already exists: ${collectionName}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`'Failed to check/create "${collectionName}" collection:`, error);
|
||||||
|
// If listCollections fails, try alternative approach
|
||||||
|
try {
|
||||||
|
// Try to access the collection directly - this will create it in MongoDB if it doesn't exist
|
||||||
|
await db.collection(collectionName).findOne({}, { projection: { _id: 1 } });
|
||||||
|
} catch (createError) {
|
||||||
|
logger.error(`Could not ensure collection ${collectionName} exists:`, createError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const db = mongoose.connection.db;
|
||||||
|
if (db) {
|
||||||
|
await ensureCollectionExists(db, 'aclentries');
|
||||||
|
}
|
||||||
|
|
||||||
// Verify required roles exist
|
// Verify required roles exist
|
||||||
const ownerRole = await db.findRoleByIdentifier(AccessRoleIds.AGENT_OWNER);
|
const ownerRole = await methods.findRoleByIdentifier(AccessRoleIds.AGENT_OWNER);
|
||||||
const viewerRole = await db.findRoleByIdentifier(AccessRoleIds.AGENT_VIEWER);
|
const viewerRole = await methods.findRoleByIdentifier(AccessRoleIds.AGENT_VIEWER);
|
||||||
const editorRole = await db.findRoleByIdentifier(AccessRoleIds.AGENT_EDITOR);
|
const editorRole = await methods.findRoleByIdentifier(AccessRoleIds.AGENT_EDITOR);
|
||||||
|
|
||||||
if (!ownerRole || !viewerRole || !editorRole) {
|
if (!ownerRole || !viewerRole || !editorRole) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
|
@ -70,7 +99,7 @@ export async function checkAgentPermissionsMigration({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get global project agent IDs
|
// Get global project agent IDs
|
||||||
const globalProject = await db.getProjectByName(GLOBAL_PROJECT_NAME, ['agentIds']);
|
const globalProject = await methods.getProjectByName(GLOBAL_PROJECT_NAME, ['agentIds']);
|
||||||
const globalAgentIds = new Set(globalProject?.agentIds || []);
|
const globalAgentIds = new Set(globalProject?.agentIds || []);
|
||||||
|
|
||||||
// Find agents without ACL entries (no batching for efficiency on startup)
|
// Find agents without ACL entries (no batching for efficiency on startup)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { logger } from '@librechat/data-schemas';
|
import { logger } from '@librechat/data-schemas';
|
||||||
import { AccessRoleIds, ResourceType, PrincipalType, Constants } from 'librechat-data-provider';
|
import { AccessRoleIds, ResourceType, PrincipalType, Constants } from 'librechat-data-provider';
|
||||||
import type { AccessRoleMethods, IPromptGroupDocument } from '@librechat/data-schemas';
|
import type { AccessRoleMethods, IPromptGroupDocument } from '@librechat/data-schemas';
|
||||||
import type { Model } from 'mongoose';
|
import type { Model, Mongoose, mongo } from 'mongoose';
|
||||||
|
|
||||||
const { GLOBAL_PROJECT_NAME } = Constants;
|
const { GLOBAL_PROJECT_NAME } = Constants;
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@ export interface PromptMigrationCheckDbMethods {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PromptMigrationCheckParams {
|
export interface PromptMigrationCheckParams {
|
||||||
db: PromptMigrationCheckDbMethods;
|
mongoose: Mongoose;
|
||||||
|
methods: PromptMigrationCheckDbMethods;
|
||||||
PromptGroupModel: Model<IPromptGroupDocument>;
|
PromptGroupModel: Model<IPromptGroupDocument>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,16 +45,45 @@ export interface PromptMigrationCheckResult {
|
||||||
* This performs a dry-run check similar to the migration script
|
* This performs a dry-run check similar to the migration script
|
||||||
*/
|
*/
|
||||||
export async function checkPromptPermissionsMigration({
|
export async function checkPromptPermissionsMigration({
|
||||||
db,
|
methods,
|
||||||
|
mongoose,
|
||||||
PromptGroupModel,
|
PromptGroupModel,
|
||||||
}: PromptMigrationCheckParams): Promise<PromptMigrationCheckResult> {
|
}: PromptMigrationCheckParams): Promise<PromptMigrationCheckResult> {
|
||||||
logger.debug('Checking if prompt permissions migration is needed');
|
logger.debug('Checking if prompt permissions migration is needed');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
/** Ensurse `aclentries` collection exists for DocumentDB compatibility */
|
||||||
|
async function ensureCollectionExists(db: mongo.Db, collectionName: string) {
|
||||||
|
try {
|
||||||
|
const collections = await db.listCollections({ name: collectionName }).toArray();
|
||||||
|
if (collections.length === 0) {
|
||||||
|
await db.createCollection(collectionName);
|
||||||
|
logger.info(`Created collection: ${collectionName}`);
|
||||||
|
} else {
|
||||||
|
logger.debug(`Collection already exists: ${collectionName}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`'Failed to check/create "${collectionName}" collection:`, error);
|
||||||
|
// If listCollections fails, try alternative approach
|
||||||
|
try {
|
||||||
|
// Try to access the collection directly - this will create it in MongoDB if it doesn't exist
|
||||||
|
await db.collection(collectionName).findOne({}, { projection: { _id: 1 } });
|
||||||
|
} catch (createError) {
|
||||||
|
logger.error(`Could not ensure collection ${collectionName} exists:`, createError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Native MongoDB database instance */
|
||||||
|
const db = mongoose.connection.db;
|
||||||
|
if (db) {
|
||||||
|
await ensureCollectionExists(db, 'aclentries');
|
||||||
|
}
|
||||||
|
|
||||||
// Verify required roles exist
|
// Verify required roles exist
|
||||||
const ownerRole = await db.findRoleByIdentifier(AccessRoleIds.PROMPTGROUP_OWNER);
|
const ownerRole = await methods.findRoleByIdentifier(AccessRoleIds.PROMPTGROUP_OWNER);
|
||||||
const viewerRole = await db.findRoleByIdentifier(AccessRoleIds.PROMPTGROUP_VIEWER);
|
const viewerRole = await methods.findRoleByIdentifier(AccessRoleIds.PROMPTGROUP_VIEWER);
|
||||||
const editorRole = await db.findRoleByIdentifier(AccessRoleIds.PROMPTGROUP_EDITOR);
|
const editorRole = await methods.findRoleByIdentifier(AccessRoleIds.PROMPTGROUP_EDITOR);
|
||||||
|
|
||||||
if (!ownerRole || !viewerRole || !editorRole) {
|
if (!ownerRole || !viewerRole || !editorRole) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
|
@ -66,8 +96,8 @@ export async function checkPromptPermissionsMigration({
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get global project prompt group IDs
|
/** Global project prompt group IDs */
|
||||||
const globalProject = await db.getProjectByName(GLOBAL_PROJECT_NAME, ['promptGroupIds']);
|
const globalProject = await methods.getProjectByName(GLOBAL_PROJECT_NAME, ['promptGroupIds']);
|
||||||
const globalPromptGroupIds = new Set(
|
const globalPromptGroupIds = new Set(
|
||||||
(globalProject?.promptGroupIds || []).map((id) => id.toString()),
|
(globalProject?.promptGroupIds || []).map((id) => id.toString()),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue