appwrite-utils-cli 0.10.86 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.appwrite/.yaml_schemas/appwrite-config.schema.json +380 -0
- package/.appwrite/.yaml_schemas/collection.schema.json +255 -0
- package/.appwrite/collections/Categories.yaml +182 -0
- package/.appwrite/collections/ExampleCollection.yaml +36 -0
- package/.appwrite/collections/Posts.yaml +227 -0
- package/.appwrite/collections/Users.yaml +149 -0
- package/.appwrite/config.yaml +109 -0
- package/.appwrite/import/README.md +148 -0
- package/.appwrite/import/categories-import.yaml +129 -0
- package/.appwrite/import/posts-import.yaml +208 -0
- package/.appwrite/import/users-import.yaml +130 -0
- package/.appwrite/importData/categories.json +194 -0
- package/.appwrite/importData/posts.json +270 -0
- package/.appwrite/importData/users.json +220 -0
- package/.appwrite/schemas/categories.json +128 -0
- package/.appwrite/schemas/exampleCollection.json +52 -0
- package/.appwrite/schemas/posts.json +173 -0
- package/.appwrite/schemas/users.json +125 -0
- package/README.md +260 -33
- package/dist/collections/attributes.js +3 -2
- package/dist/collections/methods.js +56 -38
- package/dist/config/yamlConfig.d.ts +501 -0
- package/dist/config/yamlConfig.js +452 -0
- package/dist/databases/setup.d.ts +6 -0
- package/dist/databases/setup.js +119 -0
- package/dist/functions/methods.d.ts +1 -1
- package/dist/functions/methods.js +5 -2
- package/dist/functions/openapi.d.ts +4 -0
- package/dist/functions/openapi.js +60 -0
- package/dist/interactiveCLI.d.ts +5 -0
- package/dist/interactiveCLI.js +194 -49
- package/dist/main.js +91 -30
- package/dist/migrations/afterImportActions.js +2 -2
- package/dist/migrations/appwriteToX.d.ts +10 -0
- package/dist/migrations/appwriteToX.js +15 -4
- package/dist/migrations/backup.d.ts +16 -16
- package/dist/migrations/dataLoader.d.ts +83 -1
- package/dist/migrations/dataLoader.js +4 -4
- package/dist/migrations/importController.js +25 -18
- package/dist/migrations/importDataActions.js +2 -2
- package/dist/migrations/logging.d.ts +9 -1
- package/dist/migrations/logging.js +41 -22
- package/dist/migrations/migrationHelper.d.ts +4 -4
- package/dist/migrations/relationships.js +1 -1
- package/dist/migrations/services/DataTransformationService.d.ts +55 -0
- package/dist/migrations/services/DataTransformationService.js +158 -0
- package/dist/migrations/services/FileHandlerService.d.ts +75 -0
- package/dist/migrations/services/FileHandlerService.js +236 -0
- package/dist/migrations/services/ImportOrchestrator.d.ts +97 -0
- package/dist/migrations/services/ImportOrchestrator.js +488 -0
- package/dist/migrations/services/RateLimitManager.d.ts +138 -0
- package/dist/migrations/services/RateLimitManager.js +279 -0
- package/dist/migrations/services/RelationshipResolver.d.ts +120 -0
- package/dist/migrations/services/RelationshipResolver.js +332 -0
- package/dist/migrations/services/UserMappingService.d.ts +109 -0
- package/dist/migrations/services/UserMappingService.js +277 -0
- package/dist/migrations/services/ValidationService.d.ts +74 -0
- package/dist/migrations/services/ValidationService.js +260 -0
- package/dist/migrations/transfer.d.ts +0 -6
- package/dist/migrations/transfer.js +16 -132
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +384 -0
- package/dist/migrations/yaml/YamlImportConfigLoader.js +375 -0
- package/dist/migrations/yaml/YamlImportIntegration.d.ts +87 -0
- package/dist/migrations/yaml/YamlImportIntegration.js +330 -0
- package/dist/migrations/yaml/generateImportSchemas.d.ts +17 -0
- package/dist/migrations/yaml/generateImportSchemas.js +575 -0
- package/dist/schemas/authUser.d.ts +9 -9
- package/dist/shared/attributeManager.d.ts +17 -0
- package/dist/shared/attributeManager.js +273 -0
- package/dist/shared/confirmationDialogs.d.ts +75 -0
- package/dist/shared/confirmationDialogs.js +236 -0
- package/dist/shared/functionManager.d.ts +48 -0
- package/dist/shared/functionManager.js +322 -0
- package/dist/shared/indexManager.d.ts +24 -0
- package/dist/shared/indexManager.js +150 -0
- package/dist/shared/jsonSchemaGenerator.d.ts +51 -0
- package/dist/shared/jsonSchemaGenerator.js +313 -0
- package/dist/shared/logging.d.ts +10 -0
- package/dist/shared/logging.js +46 -0
- package/dist/shared/messageFormatter.d.ts +37 -0
- package/dist/shared/messageFormatter.js +152 -0
- package/dist/shared/migrationHelpers.d.ts +173 -0
- package/dist/shared/migrationHelpers.js +142 -0
- package/dist/shared/operationLogger.d.ts +3 -0
- package/dist/shared/operationLogger.js +25 -0
- package/dist/shared/operationQueue.d.ts +13 -0
- package/dist/shared/operationQueue.js +79 -0
- package/dist/shared/progressManager.d.ts +62 -0
- package/dist/shared/progressManager.js +215 -0
- package/dist/shared/schemaGenerator.d.ts +18 -0
- package/dist/shared/schemaGenerator.js +523 -0
- package/dist/storage/methods.d.ts +3 -1
- package/dist/storage/methods.js +144 -55
- package/dist/storage/schemas.d.ts +56 -16
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/users/methods.d.ts +16 -0
- package/dist/users/methods.js +276 -0
- package/dist/utils/configMigration.d.ts +1 -0
- package/dist/utils/configMigration.js +156 -0
- package/dist/utils/dataConverters.d.ts +46 -0
- package/dist/utils/dataConverters.js +139 -0
- package/dist/utils/loadConfigs.d.ts +15 -4
- package/dist/utils/loadConfigs.js +377 -51
- package/dist/utils/schemaStrings.js +2 -1
- package/dist/utils/setupFiles.d.ts +2 -1
- package/dist/utils/setupFiles.js +723 -28
- package/dist/utils/validationRules.d.ts +43 -0
- package/dist/utils/validationRules.js +42 -0
- package/dist/utils/yamlConverter.d.ts +48 -0
- package/dist/utils/yamlConverter.js +98 -0
- package/dist/utilsController.js +65 -43
- package/package.json +19 -15
- package/src/collections/attributes.ts +3 -2
- package/src/collections/methods.ts +85 -51
- package/src/config/yamlConfig.ts +488 -0
- package/src/{migrations/setupDatabase.ts → databases/setup.ts} +11 -5
- package/src/functions/methods.ts +8 -4
- package/src/functions/templates/count-docs-in-collection/package.json +25 -0
- package/src/functions/templates/count-docs-in-collection/tsconfig.json +28 -0
- package/src/functions/templates/typescript-node/package.json +24 -0
- package/src/functions/templates/typescript-node/tsconfig.json +28 -0
- package/src/functions/templates/uv/README.md +31 -0
- package/src/functions/templates/uv/pyproject.toml +29 -0
- package/src/interactiveCLI.ts +226 -61
- package/src/main.ts +111 -37
- package/src/migrations/afterImportActions.ts +2 -2
- package/src/migrations/appwriteToX.ts +17 -4
- package/src/migrations/dataLoader.ts +4 -4
- package/src/migrations/importController.ts +30 -22
- package/src/migrations/importDataActions.ts +2 -2
- package/src/migrations/relationships.ts +1 -1
- package/src/migrations/services/DataTransformationService.ts +196 -0
- package/src/migrations/services/FileHandlerService.ts +311 -0
- package/src/migrations/services/ImportOrchestrator.ts +669 -0
- package/src/migrations/services/RateLimitManager.ts +363 -0
- package/src/migrations/services/RelationshipResolver.ts +461 -0
- package/src/migrations/services/UserMappingService.ts +345 -0
- package/src/migrations/services/ValidationService.ts +349 -0
- package/src/migrations/transfer.ts +22 -228
- package/src/migrations/yaml/YamlImportConfigLoader.ts +427 -0
- package/src/migrations/yaml/YamlImportIntegration.ts +419 -0
- package/src/migrations/yaml/generateImportSchemas.ts +589 -0
- package/src/shared/attributeManager.ts +429 -0
- package/src/shared/confirmationDialogs.ts +327 -0
- package/src/shared/functionManager.ts +515 -0
- package/src/shared/indexManager.ts +253 -0
- package/src/shared/jsonSchemaGenerator.ts +403 -0
- package/src/shared/logging.ts +74 -0
- package/src/shared/messageFormatter.ts +195 -0
- package/src/{migrations/migrationHelper.ts → shared/migrationHelpers.ts} +22 -4
- package/src/{migrations/helper.ts → shared/operationLogger.ts} +7 -2
- package/src/{migrations/queue.ts → shared/operationQueue.ts} +1 -1
- package/src/shared/progressManager.ts +278 -0
- package/src/{migrations/schemaStrings.ts → shared/schemaGenerator.ts} +71 -17
- package/src/storage/methods.ts +199 -78
- package/src/types.ts +2 -2
- package/src/{migrations/users.ts → users/methods.ts} +2 -2
- package/src/utils/configMigration.ts +212 -0
- package/src/utils/loadConfigs.ts +414 -52
- package/src/utils/schemaStrings.ts +2 -1
- package/src/utils/setupFiles.ts +742 -40
- package/src/{migrations → utils}/validationRules.ts +1 -1
- package/src/utils/yamlConverter.ts +131 -0
- package/src/utilsController.ts +75 -54
- package/src/functions/templates/poetry/README.md +0 -30
- package/src/functions/templates/poetry/pyproject.toml +0 -16
- package/src/migrations/attributes.ts +0 -561
- package/src/migrations/backup.ts +0 -205
- package/src/migrations/databases.ts +0 -39
- package/src/migrations/dbHelpers.ts +0 -92
- package/src/migrations/indexes.ts +0 -40
- package/src/migrations/logging.ts +0 -29
- package/src/migrations/storage.ts +0 -538
- /package/src/{migrations → functions}/openapi.ts +0 -0
- /package/src/functions/templates/{poetry → uv}/src/__init__.py +0 -0
- /package/src/functions/templates/{poetry → uv}/src/index.py +0 -0
- /package/src/{migrations/converters.ts → utils/dataConverters.ts} +0 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
import winston from "winston";
|
2
|
+
import fs from "fs";
|
3
|
+
import path from "path";
|
4
|
+
|
5
|
+
export interface LoggingConfig {
|
6
|
+
enabled: boolean;
|
7
|
+
level: string;
|
8
|
+
logDirectory?: string;
|
9
|
+
console: boolean;
|
10
|
+
}
|
11
|
+
|
12
|
+
const DEFAULT_LOGGING_CONFIG: LoggingConfig = {
|
13
|
+
enabled: false,
|
14
|
+
level: "info",
|
15
|
+
console: false,
|
16
|
+
};
|
17
|
+
|
18
|
+
let loggingConfig: LoggingConfig = DEFAULT_LOGGING_CONFIG;
|
19
|
+
|
20
|
+
export const configureLogging = (config: Partial<LoggingConfig> = {}) => {
|
21
|
+
loggingConfig = { ...DEFAULT_LOGGING_CONFIG, ...config };
|
22
|
+
};
|
23
|
+
|
24
|
+
const createLogger = () => {
|
25
|
+
const transports: winston.transport[] = [];
|
26
|
+
|
27
|
+
// Add console transport if enabled
|
28
|
+
if (loggingConfig.console) {
|
29
|
+
transports.push(new winston.transports.Console({
|
30
|
+
format: winston.format.combine(
|
31
|
+
winston.format.colorize(),
|
32
|
+
winston.format.simple()
|
33
|
+
)
|
34
|
+
}));
|
35
|
+
}
|
36
|
+
|
37
|
+
// Add file transports if logging is enabled
|
38
|
+
if (loggingConfig.enabled) {
|
39
|
+
const logDir = loggingConfig.logDirectory || path.join(process.cwd(), "zlogs");
|
40
|
+
|
41
|
+
if (!fs.existsSync(logDir)) {
|
42
|
+
fs.mkdirSync(logDir, { recursive: true });
|
43
|
+
}
|
44
|
+
|
45
|
+
transports.push(
|
46
|
+
new winston.transports.File({
|
47
|
+
filename: path.join(logDir, "error.log"),
|
48
|
+
level: "error",
|
49
|
+
}),
|
50
|
+
new winston.transports.File({
|
51
|
+
filename: path.join(logDir, "combined.log"),
|
52
|
+
})
|
53
|
+
);
|
54
|
+
}
|
55
|
+
|
56
|
+
return winston.createLogger({
|
57
|
+
level: loggingConfig.level,
|
58
|
+
format: winston.format.combine(
|
59
|
+
winston.format.timestamp(),
|
60
|
+
winston.format.errors({ stack: true }),
|
61
|
+
winston.format.json()
|
62
|
+
),
|
63
|
+
defaultMeta: { service: "appwrite-utils-cli" },
|
64
|
+
transports,
|
65
|
+
silent: !loggingConfig.enabled && !loggingConfig.console,
|
66
|
+
});
|
67
|
+
};
|
68
|
+
|
69
|
+
export let logger = createLogger();
|
70
|
+
|
71
|
+
// Recreate logger when config changes
|
72
|
+
export const updateLogger = () => {
|
73
|
+
logger = createLogger();
|
74
|
+
};
|
@@ -0,0 +1,195 @@
|
|
1
|
+
import chalk from "chalk";
|
2
|
+
import { logger } from "./logging.js";
|
3
|
+
|
4
|
+
export interface MessageOptions {
|
5
|
+
prefix?: string;
|
6
|
+
skipLogging?: boolean;
|
7
|
+
logLevel?: "info" | "warn" | "error" | "debug";
|
8
|
+
}
|
9
|
+
|
10
|
+
export class MessageFormatter {
|
11
|
+
static success(message: string, options: MessageOptions = {}) {
|
12
|
+
const formatted = `${chalk.green("✅")} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
|
13
|
+
console.log(formatted);
|
14
|
+
|
15
|
+
if (!options.skipLogging) {
|
16
|
+
logger.info(`SUCCESS: ${options.prefix ? `${options.prefix}: ` : ""}${message}`);
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
static error(message: string, error?: Error | string, options: MessageOptions = {}) {
|
21
|
+
const errorDetails = error instanceof Error ? error.message : error;
|
22
|
+
const formatted = `${chalk.red("❌")} ${options.prefix ? `${options.prefix}: ` : ""}${message}${errorDetails ? `\n ${chalk.gray(errorDetails)}` : ""}`;
|
23
|
+
console.error(formatted);
|
24
|
+
|
25
|
+
if (!options.skipLogging) {
|
26
|
+
logger.error(`ERROR: ${options.prefix ? `${options.prefix}: ` : ""}${message}`, {
|
27
|
+
error: errorDetails,
|
28
|
+
stack: error instanceof Error ? error.stack : undefined,
|
29
|
+
});
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
static warning(message: string, options: MessageOptions = {}) {
|
34
|
+
const formatted = `${chalk.yellow("⚠️")} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
|
35
|
+
console.log(formatted);
|
36
|
+
|
37
|
+
if (!options.skipLogging) {
|
38
|
+
logger.warn(`WARNING: ${options.prefix ? `${options.prefix}: ` : ""}${message}`);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
static info(message: string, options: MessageOptions = {}) {
|
43
|
+
const formatted = `${chalk.blue("ℹ️")} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
|
44
|
+
console.log(formatted);
|
45
|
+
|
46
|
+
if (!options.skipLogging) {
|
47
|
+
logger.info(`INFO: ${options.prefix ? `${options.prefix}: ` : ""}${message}`);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
static step(step: number, total: number, message: string, options: MessageOptions = {}) {
|
52
|
+
const formatted = `${chalk.cyan(`[${step}/${total}]`)} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
|
53
|
+
console.log(formatted);
|
54
|
+
|
55
|
+
if (!options.skipLogging) {
|
56
|
+
logger.info(`STEP ${step}/${total}: ${options.prefix ? `${options.prefix}: ` : ""}${message}`);
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
static progress(message: string, options: MessageOptions = {}) {
|
61
|
+
const formatted = `${chalk.gray("⏳")} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
|
62
|
+
console.log(formatted);
|
63
|
+
|
64
|
+
if (!options.skipLogging) {
|
65
|
+
logger.debug(`PROGRESS: ${options.prefix ? `${options.prefix}: ` : ""}${message}`);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
static debug(message: string, data?: any, options: MessageOptions = {}) {
|
70
|
+
if (process.env.NODE_ENV === "development" || process.env.DEBUG) {
|
71
|
+
const formatted = `${chalk.magenta("🔍")} ${options.prefix ? `${options.prefix}: ` : ""}${message}`;
|
72
|
+
console.log(formatted);
|
73
|
+
if (data) {
|
74
|
+
console.log(chalk.gray(JSON.stringify(data, null, 2)));
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
if (!options.skipLogging) {
|
79
|
+
logger.debug(`DEBUG: ${options.prefix ? `${options.prefix}: ` : ""}${message}`, data);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
static banner(title: string, subtitle?: string) {
|
84
|
+
const divider = chalk.cyan("═".repeat(60));
|
85
|
+
console.log(`\n${divider}`);
|
86
|
+
console.log(chalk.cyan.bold(` ${title}`));
|
87
|
+
if (subtitle) {
|
88
|
+
console.log(chalk.gray(` ${subtitle}`));
|
89
|
+
}
|
90
|
+
console.log(`${divider}\n`);
|
91
|
+
}
|
92
|
+
|
93
|
+
static section(title: string) {
|
94
|
+
console.log(`\n${chalk.bold.underline(title)}`);
|
95
|
+
}
|
96
|
+
|
97
|
+
static list(items: string[], title?: string) {
|
98
|
+
if (title) {
|
99
|
+
console.log(chalk.bold(title));
|
100
|
+
}
|
101
|
+
items.forEach((item, index) => {
|
102
|
+
console.log(`${chalk.gray(` ${index + 1}.`)} ${item}`);
|
103
|
+
});
|
104
|
+
}
|
105
|
+
|
106
|
+
static table(data: Record<string, string | number>[], headers?: string[]) {
|
107
|
+
if (data.length === 0) return;
|
108
|
+
|
109
|
+
const keys = headers || Object.keys(data[0]);
|
110
|
+
const maxWidths = keys.map(key =>
|
111
|
+
Math.max(
|
112
|
+
key.length,
|
113
|
+
...data.map(row => String(row[key]).length)
|
114
|
+
)
|
115
|
+
);
|
116
|
+
|
117
|
+
// Header
|
118
|
+
const headerRow = keys.map((key, i) =>
|
119
|
+
chalk.bold(key.padEnd(maxWidths[i]))
|
120
|
+
).join(" │ ");
|
121
|
+
console.log(`┌─${keys.map((_, i) => "─".repeat(maxWidths[i])).join("─┼─")}─┐`);
|
122
|
+
console.log(`│ ${headerRow} │`);
|
123
|
+
console.log(`├─${keys.map((_, i) => "─".repeat(maxWidths[i])).join("─┼─")}─┤`);
|
124
|
+
|
125
|
+
// Rows
|
126
|
+
data.forEach(row => {
|
127
|
+
const dataRow = keys.map((key, i) =>
|
128
|
+
String(row[key]).padEnd(maxWidths[i])
|
129
|
+
).join(" │ ");
|
130
|
+
console.log(`│ ${dataRow} │`);
|
131
|
+
});
|
132
|
+
console.log(`└─${keys.map((_, i) => "─".repeat(maxWidths[i])).join("─┴─")}─┘`);
|
133
|
+
}
|
134
|
+
|
135
|
+
static operationSummary(title: string, stats: Record<string, string | number>, duration?: number) {
|
136
|
+
this.section(`${title} Summary`);
|
137
|
+
|
138
|
+
Object.entries(stats).forEach(([key, value]) => {
|
139
|
+
const formattedKey = key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase());
|
140
|
+
console.log(`${chalk.gray("●")} ${formattedKey}: ${chalk.cyan(String(value))}`);
|
141
|
+
});
|
142
|
+
|
143
|
+
if (duration) {
|
144
|
+
console.log(`${chalk.gray("●")} Duration: ${chalk.cyan(this.formatDuration(duration))}`);
|
145
|
+
}
|
146
|
+
console.log();
|
147
|
+
}
|
148
|
+
|
149
|
+
static formatBytes(bytes: number): string {
|
150
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
151
|
+
let size = bytes;
|
152
|
+
let unitIndex = 0;
|
153
|
+
|
154
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
155
|
+
size /= 1024;
|
156
|
+
unitIndex++;
|
157
|
+
}
|
158
|
+
|
159
|
+
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
160
|
+
}
|
161
|
+
|
162
|
+
static formatDuration(ms: number): string {
|
163
|
+
const seconds = Math.floor(ms / 1000);
|
164
|
+
const minutes = Math.floor(seconds / 60);
|
165
|
+
const hours = Math.floor(minutes / 60);
|
166
|
+
|
167
|
+
if (hours > 0) {
|
168
|
+
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
169
|
+
} else if (minutes > 0) {
|
170
|
+
return `${minutes}m ${seconds % 60}s`;
|
171
|
+
} else {
|
172
|
+
return `${seconds}s`;
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
static formatNumber(num: number): string {
|
177
|
+
return num.toLocaleString();
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
export const Messages = {
|
182
|
+
CONFIG_NOT_FOUND: "Appwrite configuration not found. Run 'appwrite-migrate setup' first.",
|
183
|
+
CONFIG_LOADED: (type: string, path: string) => `Loaded ${type} configuration from ${path}`,
|
184
|
+
DATABASE_CONNECTION_FAILED: "Failed to connect to Appwrite. Check your endpoint and API key.",
|
185
|
+
OPERATION_CANCELLED: "Operation cancelled by user.",
|
186
|
+
OPERATION_COMPLETED: (operation: string) => `${operation} completed successfully`,
|
187
|
+
BACKUP_STARTED: (database: string) => `Starting backup for database: ${database}`,
|
188
|
+
BACKUP_COMPLETED: (database: string, size: number) => `Backup completed for ${database} (${MessageFormatter.formatBytes(size)})`,
|
189
|
+
IMPORT_STARTED: (collections: number) => `Starting import process for ${collections} collection(s)`,
|
190
|
+
IMPORT_COMPLETED: (documents: number) => `Import completed. Processed ${MessageFormatter.formatNumber(documents)} documents`,
|
191
|
+
FUNCTION_DEPLOYED: (name: string) => `Function '${name}' deployed successfully`,
|
192
|
+
FUNCTION_DEPLOYMENT_FAILED: (name: string, error: string) => `Function '${name}' deployment failed: ${error}`,
|
193
|
+
TRANSFER_STARTED: (source: string, target: string) => `Starting transfer from ${source} to ${target}`,
|
194
|
+
TRANSFER_COMPLETED: (items: number) => `Transfer completed. Moved ${MessageFormatter.formatNumber(items)} items`,
|
195
|
+
} as const;
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { ID, Query, type Databases } from "node-appwrite";
|
2
|
-
import { BatchSchema, OperationSchema, type Operation } from "
|
2
|
+
import { BatchSchema, OperationSchema, type Operation } from "../storage/schemas.js";
|
3
3
|
import { AttributeMappingsSchema } from "appwrite-utils";
|
4
4
|
import { z } from "zod";
|
5
5
|
import { logger } from "./logging.js";
|
@@ -29,8 +29,14 @@ export type ContextObject = z.infer<typeof ContextObject>;
|
|
29
29
|
export const createOrFindAfterImportOperation = async (
|
30
30
|
database: Databases,
|
31
31
|
collectionId: string,
|
32
|
-
context: ContextObject
|
32
|
+
context: ContextObject,
|
33
|
+
useMigrations: boolean = true
|
33
34
|
) => {
|
35
|
+
if (!useMigrations) {
|
36
|
+
logger.info("Migrations disabled, skipping after import operation tracking");
|
37
|
+
return;
|
38
|
+
}
|
39
|
+
|
34
40
|
let operation = await findOrCreateOperation(
|
35
41
|
database,
|
36
42
|
collectionId,
|
@@ -69,8 +75,14 @@ export const addBatch = async (database: Databases, data: string) => {
|
|
69
75
|
|
70
76
|
export const getAfterImportOperations = async (
|
71
77
|
database: Databases,
|
72
|
-
collectionId: string
|
78
|
+
collectionId: string,
|
79
|
+
useMigrations: boolean = true
|
73
80
|
) => {
|
81
|
+
if (!useMigrations) {
|
82
|
+
logger.info("Migrations disabled, returning empty operations list");
|
83
|
+
return [];
|
84
|
+
}
|
85
|
+
|
74
86
|
let lastDocumentId: string | undefined;
|
75
87
|
const allOperations = [];
|
76
88
|
let total = 0;
|
@@ -149,8 +161,14 @@ export const findOrCreateOperation = async (
|
|
149
161
|
export const updateOperation = async (
|
150
162
|
database: Databases,
|
151
163
|
operationId: string,
|
152
|
-
updateFields: any
|
164
|
+
updateFields: any,
|
165
|
+
useMigrations: boolean = true
|
153
166
|
) => {
|
167
|
+
if (!useMigrations) {
|
168
|
+
logger.info("Migrations disabled, skipping operation update");
|
169
|
+
return;
|
170
|
+
}
|
171
|
+
|
154
172
|
await tryAwaitWithRetry(
|
155
173
|
async () =>
|
156
174
|
await database.updateDocument(
|
@@ -7,8 +7,13 @@ export const logOperation = async (
|
|
7
7
|
db: Databases,
|
8
8
|
dbId: string,
|
9
9
|
operationDetails: OperationCreate,
|
10
|
-
operationId?: string
|
11
|
-
|
10
|
+
operationId?: string,
|
11
|
+
useMigrations: boolean = true
|
12
|
+
): Promise<Models.Document | null> => {
|
13
|
+
if (!useMigrations) {
|
14
|
+
console.log("Migrations disabled, skipping operation logging");
|
15
|
+
return null;
|
16
|
+
}
|
12
17
|
try {
|
13
18
|
let operation;
|
14
19
|
if (operationId) {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Query, type Databases, type Models } from "node-appwrite";
|
2
2
|
import type { Attribute } from "appwrite-utils";
|
3
|
-
import { createOrUpdateAttribute } from "
|
3
|
+
import { createOrUpdateAttribute } from "../collections/attributes.js";
|
4
4
|
import { fetchAndCacheCollectionByName } from "../collections/methods.js";
|
5
5
|
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
6
6
|
|
@@ -0,0 +1,278 @@
|
|
1
|
+
import cliProgress from "cli-progress";
|
2
|
+
import chalk from "chalk";
|
3
|
+
import { MessageFormatter } from "./messageFormatter.js";
|
4
|
+
|
5
|
+
export interface ProgressOptions {
|
6
|
+
title?: string;
|
7
|
+
showETA?: boolean;
|
8
|
+
showSpeed?: boolean;
|
9
|
+
showPercentage?: boolean;
|
10
|
+
width?: number;
|
11
|
+
format?: string;
|
12
|
+
}
|
13
|
+
|
14
|
+
export class ProgressManager {
|
15
|
+
private static instances = new Map<string, ProgressManager>();
|
16
|
+
private bar: cliProgress.SingleBar;
|
17
|
+
private startTime: number;
|
18
|
+
private totalItems: number;
|
19
|
+
private completed: number = 0;
|
20
|
+
private id: string;
|
21
|
+
private title: string;
|
22
|
+
|
23
|
+
private constructor(id: string, total: number, options: ProgressOptions = {}) {
|
24
|
+
this.id = id;
|
25
|
+
this.totalItems = total;
|
26
|
+
this.startTime = Date.now();
|
27
|
+
this.title = options.title || "Processing";
|
28
|
+
|
29
|
+
const format = options.format ||
|
30
|
+
`${chalk.cyan(this.title)} ${chalk.yellow("[{bar}]")} {percentage}% | {value}/{total} | ETA: {eta}s | Speed: {speed}/s`;
|
31
|
+
|
32
|
+
this.bar = new cliProgress.SingleBar({
|
33
|
+
format,
|
34
|
+
barCompleteChar: '█',
|
35
|
+
barIncompleteChar: '░',
|
36
|
+
hideCursor: true,
|
37
|
+
clearOnComplete: false,
|
38
|
+
stopOnComplete: true,
|
39
|
+
...options,
|
40
|
+
}, cliProgress.Presets.shades_classic);
|
41
|
+
|
42
|
+
this.bar.start(total, 0);
|
43
|
+
}
|
44
|
+
|
45
|
+
static create(id: string, total: number, options: ProgressOptions = {}): ProgressManager {
|
46
|
+
if (ProgressManager.instances.has(id)) {
|
47
|
+
const existing = ProgressManager.instances.get(id)!;
|
48
|
+
existing.stop();
|
49
|
+
}
|
50
|
+
|
51
|
+
const instance = new ProgressManager(id, total, options);
|
52
|
+
ProgressManager.instances.set(id, instance);
|
53
|
+
return instance;
|
54
|
+
}
|
55
|
+
|
56
|
+
static get(id: string): ProgressManager | undefined {
|
57
|
+
return ProgressManager.instances.get(id);
|
58
|
+
}
|
59
|
+
|
60
|
+
update(current: number, detail?: string) {
|
61
|
+
this.completed = current;
|
62
|
+
|
63
|
+
// Calculate speed
|
64
|
+
const elapsed = (Date.now() - this.startTime) / 1000;
|
65
|
+
const speed = elapsed > 0 ? Math.round(current / elapsed) : 0;
|
66
|
+
|
67
|
+
this.bar.update(current, {
|
68
|
+
speed,
|
69
|
+
detail: detail || '',
|
70
|
+
});
|
71
|
+
|
72
|
+
if (detail) {
|
73
|
+
// Update the payload for custom formatting
|
74
|
+
this.bar.update(current, { detail });
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
increment(amount: number = 1, detail?: string) {
|
79
|
+
this.update(this.completed + amount, detail);
|
80
|
+
}
|
81
|
+
|
82
|
+
setTotal(total: number) {
|
83
|
+
this.totalItems = total;
|
84
|
+
this.bar.setTotal(total);
|
85
|
+
}
|
86
|
+
|
87
|
+
stop(showSummary: boolean = true) {
|
88
|
+
this.bar.stop();
|
89
|
+
|
90
|
+
if (showSummary) {
|
91
|
+
const duration = Date.now() - this.startTime;
|
92
|
+
const rate = this.completed / (duration / 1000);
|
93
|
+
|
94
|
+
MessageFormatter.success(
|
95
|
+
`${this.title} completed`,
|
96
|
+
{
|
97
|
+
prefix: `${this.completed}/${this.totalItems} items in ${MessageFormatter.formatDuration(duration)} (${rate.toFixed(1)}/s)`
|
98
|
+
}
|
99
|
+
);
|
100
|
+
}
|
101
|
+
|
102
|
+
ProgressManager.instances.delete(this.id);
|
103
|
+
}
|
104
|
+
|
105
|
+
fail(error: string) {
|
106
|
+
this.bar.stop();
|
107
|
+
MessageFormatter.error(`${this.title} failed: ${error}`);
|
108
|
+
ProgressManager.instances.delete(this.id);
|
109
|
+
}
|
110
|
+
|
111
|
+
getStats() {
|
112
|
+
const duration = Date.now() - this.startTime;
|
113
|
+
const rate = this.completed / (duration / 1000);
|
114
|
+
|
115
|
+
return {
|
116
|
+
completed: this.completed,
|
117
|
+
total: this.totalItems,
|
118
|
+
percentage: (this.completed / this.totalItems) * 100,
|
119
|
+
duration,
|
120
|
+
rate,
|
121
|
+
remaining: this.totalItems - this.completed,
|
122
|
+
eta: this.completed > 0 ? ((this.totalItems - this.completed) / rate) * 1000 : 0,
|
123
|
+
};
|
124
|
+
}
|
125
|
+
|
126
|
+
static stopAll() {
|
127
|
+
for (const [id, instance] of ProgressManager.instances) {
|
128
|
+
instance.stop(false);
|
129
|
+
}
|
130
|
+
ProgressManager.instances.clear();
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
export class MultiProgressManager {
|
135
|
+
private multiBar: cliProgress.MultiBar;
|
136
|
+
private bars = new Map<string, cliProgress.SingleBar>();
|
137
|
+
private startTime: number;
|
138
|
+
|
139
|
+
constructor(options: ProgressOptions = {}) {
|
140
|
+
this.startTime = Date.now();
|
141
|
+
|
142
|
+
this.multiBar = new cliProgress.MultiBar({
|
143
|
+
clearOnComplete: false,
|
144
|
+
hideCursor: true,
|
145
|
+
format: options.format || `${chalk.cyan("{title}")} ${chalk.yellow("[{bar}]")} {percentage}% | {value}/{total} | {detail}`,
|
146
|
+
barCompleteChar: '█',
|
147
|
+
barIncompleteChar: '░',
|
148
|
+
}, cliProgress.Presets.shades_classic);
|
149
|
+
}
|
150
|
+
|
151
|
+
addTask(id: string, total: number, title: string): void {
|
152
|
+
const bar = this.multiBar.create(total, 0, {
|
153
|
+
title: title.padEnd(20),
|
154
|
+
detail: '',
|
155
|
+
});
|
156
|
+
this.bars.set(id, bar);
|
157
|
+
}
|
158
|
+
|
159
|
+
updateTask(id: string, current: number, detail?: string): void {
|
160
|
+
const bar = this.bars.get(id);
|
161
|
+
if (bar) {
|
162
|
+
bar.update(current, {
|
163
|
+
detail: detail || '',
|
164
|
+
});
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
incrementTask(id: string, amount: number = 1, detail?: string): void {
|
169
|
+
const bar = this.bars.get(id);
|
170
|
+
if (bar) {
|
171
|
+
const currentValue = bar.getProgress() * bar.getTotal();
|
172
|
+
this.updateTask(id, currentValue + amount, detail);
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
completeTask(id: string): void {
|
177
|
+
const bar = this.bars.get(id);
|
178
|
+
if (bar) {
|
179
|
+
bar.update(bar.getTotal());
|
180
|
+
}
|
181
|
+
}
|
182
|
+
|
183
|
+
failTask(id: string, error: string): void {
|
184
|
+
const bar = this.bars.get(id);
|
185
|
+
if (bar) {
|
186
|
+
bar.update(bar.getProgress() * bar.getTotal(), {
|
187
|
+
detail: chalk.red(`Failed: ${error}`),
|
188
|
+
});
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
stop(showSummary: boolean = true): void {
|
193
|
+
this.multiBar.stop();
|
194
|
+
|
195
|
+
if (showSummary) {
|
196
|
+
const duration = Date.now() - this.startTime;
|
197
|
+
MessageFormatter.success(
|
198
|
+
`All tasks completed in ${MessageFormatter.formatDuration(duration)}`
|
199
|
+
);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
getTaskStats(id: string) {
|
204
|
+
const bar = this.bars.get(id);
|
205
|
+
if (!bar) return null;
|
206
|
+
|
207
|
+
const progress = bar.getProgress();
|
208
|
+
const total = bar.getTotal();
|
209
|
+
const completed = Math.floor(progress * total);
|
210
|
+
|
211
|
+
return {
|
212
|
+
completed,
|
213
|
+
total,
|
214
|
+
percentage: progress * 100,
|
215
|
+
remaining: total - completed,
|
216
|
+
};
|
217
|
+
}
|
218
|
+
|
219
|
+
getAllStats() {
|
220
|
+
const stats = new Map<string, any>();
|
221
|
+
for (const [id, bar] of this.bars) {
|
222
|
+
stats.set(id, this.getTaskStats(id));
|
223
|
+
}
|
224
|
+
return stats;
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
// Utility functions for common progress scenarios
|
229
|
+
export const ProgressUtils = {
|
230
|
+
async withProgress<T>(
|
231
|
+
id: string,
|
232
|
+
total: number,
|
233
|
+
title: string,
|
234
|
+
operation: (progress: ProgressManager) => Promise<T>
|
235
|
+
): Promise<T> {
|
236
|
+
const progress = ProgressManager.create(id, total, { title });
|
237
|
+
try {
|
238
|
+
const result = await operation(progress);
|
239
|
+
progress.stop();
|
240
|
+
return result;
|
241
|
+
} catch (error) {
|
242
|
+
progress.fail(error instanceof Error ? error.message : String(error));
|
243
|
+
throw error;
|
244
|
+
}
|
245
|
+
},
|
246
|
+
|
247
|
+
async processArrayWithProgress<T, R>(
|
248
|
+
items: T[],
|
249
|
+
processor: (item: T, index: number) => Promise<R>,
|
250
|
+
options: { title?: string; batchSize?: number; showDetail?: boolean } = {}
|
251
|
+
): Promise<R[]> {
|
252
|
+
const { title = "Processing items", batchSize = 1, showDetail = true } = options;
|
253
|
+
const progress = ProgressManager.create(`process-${Date.now()}`, items.length, { title });
|
254
|
+
|
255
|
+
const results: R[] = [];
|
256
|
+
|
257
|
+
try {
|
258
|
+
for (let i = 0; i < items.length; i += batchSize) {
|
259
|
+
const batch = items.slice(i, i + batchSize);
|
260
|
+
const batchResults = await Promise.all(
|
261
|
+
batch.map(async (item, batchIndex) => {
|
262
|
+
const result = await processor(item, i + batchIndex);
|
263
|
+
const detail = showDetail ? `Item ${i + batchIndex + 1}: ${String(item).slice(0, 30)}...` : undefined;
|
264
|
+
progress.update(i + batchIndex + 1, detail);
|
265
|
+
return result;
|
266
|
+
})
|
267
|
+
);
|
268
|
+
results.push(...batchResults);
|
269
|
+
}
|
270
|
+
|
271
|
+
progress.stop();
|
272
|
+
return results;
|
273
|
+
} catch (error) {
|
274
|
+
progress.fail(error instanceof Error ? error.message : String(error));
|
275
|
+
throw error;
|
276
|
+
}
|
277
|
+
},
|
278
|
+
};
|