appwrite-utils-cli 1.5.2 → 1.6.0
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/CHANGELOG.md +199 -0
- package/README.md +251 -29
- package/dist/adapters/AdapterFactory.d.ts +10 -3
- package/dist/adapters/AdapterFactory.js +213 -17
- package/dist/adapters/TablesDBAdapter.js +60 -17
- package/dist/backups/operations/bucketBackup.d.ts +19 -0
- package/dist/backups/operations/bucketBackup.js +197 -0
- package/dist/backups/operations/collectionBackup.d.ts +30 -0
- package/dist/backups/operations/collectionBackup.js +201 -0
- package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
- package/dist/backups/operations/comprehensiveBackup.js +238 -0
- package/dist/backups/schemas/bucketManifest.d.ts +93 -0
- package/dist/backups/schemas/bucketManifest.js +33 -0
- package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
- package/dist/backups/schemas/comprehensiveManifest.js +32 -0
- package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
- package/dist/backups/tracking/centralizedTracking.js +274 -0
- package/dist/cli/commands/configCommands.d.ts +8 -0
- package/dist/cli/commands/configCommands.js +160 -0
- package/dist/cli/commands/databaseCommands.d.ts +13 -0
- package/dist/cli/commands/databaseCommands.js +478 -0
- package/dist/cli/commands/functionCommands.d.ts +7 -0
- package/dist/cli/commands/functionCommands.js +289 -0
- package/dist/cli/commands/schemaCommands.d.ts +7 -0
- package/dist/cli/commands/schemaCommands.js +134 -0
- package/dist/cli/commands/transferCommands.d.ts +5 -0
- package/dist/cli/commands/transferCommands.js +384 -0
- package/dist/collections/attributes.d.ts +5 -4
- package/dist/collections/attributes.js +539 -246
- package/dist/collections/indexes.js +39 -37
- package/dist/collections/methods.d.ts +2 -16
- package/dist/collections/methods.js +90 -538
- package/dist/collections/transferOperations.d.ts +7 -0
- package/dist/collections/transferOperations.js +331 -0
- package/dist/collections/wipeOperations.d.ts +16 -0
- package/dist/collections/wipeOperations.js +328 -0
- package/dist/config/configMigration.d.ts +87 -0
- package/dist/config/configMigration.js +390 -0
- package/dist/config/configValidation.d.ts +66 -0
- package/dist/config/configValidation.js +358 -0
- package/dist/config/yamlConfig.d.ts +455 -1
- package/dist/config/yamlConfig.js +145 -52
- package/dist/databases/methods.js +3 -2
- package/dist/databases/setup.d.ts +1 -2
- package/dist/databases/setup.js +9 -87
- package/dist/examples/yamlTerminologyExample.d.ts +42 -0
- package/dist/examples/yamlTerminologyExample.js +269 -0
- package/dist/functions/deployments.js +11 -10
- package/dist/functions/methods.d.ts +1 -1
- package/dist/functions/methods.js +5 -4
- package/dist/init.js +9 -9
- package/dist/interactiveCLI.d.ts +8 -17
- package/dist/interactiveCLI.js +181 -1172
- package/dist/main.js +364 -21
- package/dist/migrations/afterImportActions.js +22 -30
- package/dist/migrations/appwriteToX.js +71 -25
- package/dist/migrations/dataLoader.js +35 -26
- package/dist/migrations/importController.js +29 -30
- package/dist/migrations/relationships.js +13 -12
- package/dist/migrations/services/ImportOrchestrator.js +16 -19
- package/dist/migrations/transfer.js +46 -46
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +3 -1
- package/dist/migrations/yaml/YamlImportConfigLoader.js +6 -3
- package/dist/migrations/yaml/YamlImportIntegration.d.ts +9 -3
- package/dist/migrations/yaml/YamlImportIntegration.js +22 -11
- package/dist/migrations/yaml/generateImportSchemas.d.ts +14 -1
- package/dist/migrations/yaml/generateImportSchemas.js +736 -7
- package/dist/schemas/authUser.d.ts +1 -1
- package/dist/setupController.js +3 -2
- package/dist/shared/backupMetadataSchema.d.ts +94 -0
- package/dist/shared/backupMetadataSchema.js +38 -0
- package/dist/shared/backupTracking.d.ts +18 -0
- package/dist/shared/backupTracking.js +176 -0
- package/dist/shared/confirmationDialogs.js +15 -15
- package/dist/shared/errorUtils.d.ts +54 -0
- package/dist/shared/errorUtils.js +95 -0
- package/dist/shared/functionManager.js +20 -19
- package/dist/shared/indexManager.js +12 -11
- package/dist/shared/jsonSchemaGenerator.js +10 -26
- package/dist/shared/logging.d.ts +51 -0
- package/dist/shared/logging.js +70 -0
- package/dist/shared/messageFormatter.d.ts +2 -0
- package/dist/shared/messageFormatter.js +10 -0
- package/dist/shared/migrationHelpers.d.ts +6 -16
- package/dist/shared/migrationHelpers.js +24 -21
- package/dist/shared/operationLogger.d.ts +8 -1
- package/dist/shared/operationLogger.js +11 -24
- package/dist/shared/operationQueue.d.ts +28 -1
- package/dist/shared/operationQueue.js +268 -66
- package/dist/shared/operationsTable.d.ts +26 -0
- package/dist/shared/operationsTable.js +286 -0
- package/dist/shared/operationsTableSchema.d.ts +48 -0
- package/dist/shared/operationsTableSchema.js +35 -0
- package/dist/shared/relationshipExtractor.d.ts +56 -0
- package/dist/shared/relationshipExtractor.js +138 -0
- package/dist/shared/schemaGenerator.d.ts +19 -1
- package/dist/shared/schemaGenerator.js +56 -75
- package/dist/storage/backupCompression.d.ts +20 -0
- package/dist/storage/backupCompression.js +67 -0
- package/dist/storage/methods.d.ts +16 -2
- package/dist/storage/methods.js +98 -14
- package/dist/users/methods.js +9 -8
- package/dist/utils/configDiscovery.d.ts +78 -0
- package/dist/utils/configDiscovery.js +430 -0
- package/dist/utils/directoryUtils.d.ts +22 -0
- package/dist/utils/directoryUtils.js +59 -0
- package/dist/utils/getClientFromConfig.d.ts +17 -8
- package/dist/utils/getClientFromConfig.js +162 -17
- package/dist/utils/helperFunctions.d.ts +16 -2
- package/dist/utils/helperFunctions.js +19 -5
- package/dist/utils/loadConfigs.d.ts +34 -9
- package/dist/utils/loadConfigs.js +236 -316
- package/dist/utils/pathResolvers.d.ts +53 -0
- package/dist/utils/pathResolvers.js +72 -0
- package/dist/utils/projectConfig.d.ts +119 -0
- package/dist/utils/projectConfig.js +171 -0
- package/dist/utils/retryFailedPromises.js +4 -2
- package/dist/utils/sessionAuth.d.ts +48 -0
- package/dist/utils/sessionAuth.js +164 -0
- package/dist/utils/sessionPreservationExample.d.ts +1666 -0
- package/dist/utils/sessionPreservationExample.js +101 -0
- package/dist/utils/setupFiles.js +301 -41
- package/dist/utils/typeGuards.d.ts +35 -0
- package/dist/utils/typeGuards.js +57 -0
- package/dist/utils/versionDetection.js +145 -9
- package/dist/utils/yamlConverter.d.ts +53 -3
- package/dist/utils/yamlConverter.js +232 -13
- package/dist/utils/yamlLoader.d.ts +70 -0
- package/dist/utils/yamlLoader.js +263 -0
- package/dist/utilsController.d.ts +36 -3
- package/dist/utilsController.js +186 -56
- package/package.json +12 -2
- package/src/adapters/AdapterFactory.ts +263 -35
- package/src/adapters/TablesDBAdapter.ts +225 -36
- package/src/backups/operations/bucketBackup.ts +277 -0
- package/src/backups/operations/collectionBackup.ts +310 -0
- package/src/backups/operations/comprehensiveBackup.ts +342 -0
- package/src/backups/schemas/bucketManifest.ts +78 -0
- package/src/backups/schemas/comprehensiveManifest.ts +76 -0
- package/src/backups/tracking/centralizedTracking.ts +352 -0
- package/src/cli/commands/configCommands.ts +194 -0
- package/src/cli/commands/databaseCommands.ts +635 -0
- package/src/cli/commands/functionCommands.ts +379 -0
- package/src/cli/commands/schemaCommands.ts +163 -0
- package/src/cli/commands/transferCommands.ts +457 -0
- package/src/collections/attributes.ts +900 -621
- package/src/collections/attributes.ts.backup +1555 -0
- package/src/collections/indexes.ts +116 -114
- package/src/collections/methods.ts +295 -968
- package/src/collections/transferOperations.ts +516 -0
- package/src/collections/wipeOperations.ts +501 -0
- package/src/config/README.md +274 -0
- package/src/config/configMigration.ts +575 -0
- package/src/config/configValidation.ts +445 -0
- package/src/config/yamlConfig.ts +168 -55
- package/src/databases/methods.ts +3 -2
- package/src/databases/setup.ts +11 -138
- package/src/examples/yamlTerminologyExample.ts +341 -0
- package/src/functions/deployments.ts +14 -12
- package/src/functions/methods.ts +11 -11
- package/src/functions/templates/hono-typescript/README.md +286 -0
- package/src/functions/templates/hono-typescript/package.json +26 -0
- package/src/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
- package/src/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
- package/src/functions/templates/hono-typescript/src/app.ts +180 -0
- package/src/functions/templates/hono-typescript/src/context.ts +103 -0
- package/src/functions/templates/hono-typescript/src/index.ts +54 -0
- package/src/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
- package/src/functions/templates/hono-typescript/tsconfig.json +20 -0
- package/src/functions/templates/typescript-node/package.json +2 -1
- package/src/functions/templates/typescript-node/src/context.ts +103 -0
- package/src/functions/templates/typescript-node/src/index.ts +18 -12
- package/src/functions/templates/uv/pyproject.toml +1 -0
- package/src/functions/templates/uv/src/context.py +125 -0
- package/src/functions/templates/uv/src/index.py +35 -5
- package/src/init.ts +9 -11
- package/src/interactiveCLI.ts +278 -1596
- package/src/main.ts +418 -24
- package/src/migrations/afterImportActions.ts +71 -44
- package/src/migrations/appwriteToX.ts +100 -34
- package/src/migrations/dataLoader.ts +48 -34
- package/src/migrations/importController.ts +44 -39
- package/src/migrations/relationships.ts +28 -18
- package/src/migrations/services/ImportOrchestrator.ts +24 -27
- package/src/migrations/transfer.ts +159 -121
- package/src/migrations/yaml/YamlImportConfigLoader.ts +11 -4
- package/src/migrations/yaml/YamlImportIntegration.ts +47 -20
- package/src/migrations/yaml/generateImportSchemas.ts +751 -12
- package/src/setupController.ts +3 -2
- package/src/shared/backupMetadataSchema.ts +93 -0
- package/src/shared/backupTracking.ts +211 -0
- package/src/shared/confirmationDialogs.ts +19 -19
- package/src/shared/errorUtils.ts +110 -0
- package/src/shared/functionManager.ts +21 -20
- package/src/shared/indexManager.ts +12 -11
- package/src/shared/jsonSchemaGenerator.ts +38 -52
- package/src/shared/logging.ts +75 -0
- package/src/shared/messageFormatter.ts +14 -1
- package/src/shared/migrationHelpers.ts +45 -38
- package/src/shared/operationLogger.ts +11 -36
- package/src/shared/operationQueue.ts +322 -93
- package/src/shared/operationsTable.ts +338 -0
- package/src/shared/operationsTableSchema.ts +60 -0
- package/src/shared/relationshipExtractor.ts +214 -0
- package/src/shared/schemaGenerator.ts +179 -219
- package/src/storage/backupCompression.ts +88 -0
- package/src/storage/methods.ts +131 -34
- package/src/users/methods.ts +11 -9
- package/src/utils/configDiscovery.ts +502 -0
- package/src/utils/directoryUtils.ts +61 -0
- package/src/utils/getClientFromConfig.ts +205 -22
- package/src/utils/helperFunctions.ts +23 -5
- package/src/utils/loadConfigs.ts +313 -345
- package/src/utils/pathResolvers.ts +81 -0
- package/src/utils/projectConfig.ts +299 -0
- package/src/utils/retryFailedPromises.ts +4 -2
- package/src/utils/sessionAuth.ts +230 -0
- package/src/utils/setupFiles.ts +322 -54
- package/src/utils/typeGuards.ts +65 -0
- package/src/utils/versionDetection.ts +218 -64
- package/src/utils/yamlConverter.ts +296 -13
- package/src/utils/yamlLoader.ts +364 -0
- package/src/utilsController.ts +314 -110
- package/tests/README.md +497 -0
- package/tests/adapters/AdapterFactory.test.ts +277 -0
- package/tests/integration/syncOperations.test.ts +463 -0
- package/tests/jest.config.js +25 -0
- package/tests/migration/configMigration.test.ts +546 -0
- package/tests/setup.ts +62 -0
- package/tests/testUtils.ts +340 -0
- package/tests/utils/loadConfigs.test.ts +350 -0
- package/tests/validation/configValidation.test.ts +412 -0
- package/src/utils/schemaStrings.ts +0 -517
@@ -0,0 +1,103 @@
|
|
1
|
+
import { z } from "zod";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Appwrite Request Headers Schema
|
5
|
+
*/
|
6
|
+
export const AppwriteHeadersSchema = z.object({
|
7
|
+
"x-appwrite-trigger": z.enum(["http", "schedule", "event"]).optional(),
|
8
|
+
"x-appwrite-event": z.string().optional(),
|
9
|
+
"x-appwrite-key": z.string().optional(),
|
10
|
+
"x-appwrite-user-id": z.string().optional(),
|
11
|
+
"x-appwrite-user-jwt": z.string().optional(),
|
12
|
+
"x-appwrite-country-code": z.string().optional(),
|
13
|
+
"x-appwrite-continent-code": z.string().optional(),
|
14
|
+
"x-appwrite-continent-eu": z.string().optional(),
|
15
|
+
}).catchall(z.string()); // Allow additional headers
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Appwrite Environment Variables Schema
|
19
|
+
*/
|
20
|
+
export const AppwriteEnvSchema = z.object({
|
21
|
+
APPWRITE_FUNCTION_API_ENDPOINT: z.string(),
|
22
|
+
APPWRITE_VERSION: z.string(),
|
23
|
+
APPWRITE_REGION: z.string(),
|
24
|
+
APPWRITE_FUNCTION_API_KEY: z.string().optional(),
|
25
|
+
APPWRITE_FUNCTION_ID: z.string(),
|
26
|
+
APPWRITE_FUNCTION_NAME: z.string(),
|
27
|
+
APPWRITE_FUNCTION_DEPLOYMENT: z.string(),
|
28
|
+
APPWRITE_FUNCTION_PROJECT_ID: z.string(),
|
29
|
+
APPWRITE_FUNCTION_RUNTIME_NAME: z.string(),
|
30
|
+
APPWRITE_FUNCTION_RUNTIME_VERSION: z.string(),
|
31
|
+
});
|
32
|
+
|
33
|
+
/**
|
34
|
+
* Appwrite Request Schema (full version from docs)
|
35
|
+
*/
|
36
|
+
export const AppwriteRequestSchema = z.object({
|
37
|
+
bodyText: z.string().optional(),
|
38
|
+
bodyJson: z.union([z.any(), z.string()]).transform((t) => {
|
39
|
+
if (typeof t === "string") {
|
40
|
+
try {
|
41
|
+
return JSON.parse(t);
|
42
|
+
} catch {
|
43
|
+
return null;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
return t;
|
47
|
+
}).optional(),
|
48
|
+
body: z.any().optional(),
|
49
|
+
headers: AppwriteHeadersSchema,
|
50
|
+
scheme: z.enum(["http", "https"]).optional(),
|
51
|
+
method: z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]),
|
52
|
+
url: z.string().optional(),
|
53
|
+
host: z.string().optional(),
|
54
|
+
port: z.number().or(z.string()).optional(),
|
55
|
+
path: z.string().optional(),
|
56
|
+
queryString: z.string().optional(),
|
57
|
+
query: z.record(z.string(), z.any()).optional(),
|
58
|
+
variables: z.record(z.string(), z.any()).optional(),
|
59
|
+
text: z.any().optional(),
|
60
|
+
payload: z.string().optional(),
|
61
|
+
});
|
62
|
+
|
63
|
+
/**
|
64
|
+
* Appwrite Response Schema
|
65
|
+
*/
|
66
|
+
export const AppwriteResponseSchema = z.object({
|
67
|
+
json: z.any(),
|
68
|
+
text: z.any(),
|
69
|
+
empty: z.any().optional(),
|
70
|
+
binary: z.any().optional(),
|
71
|
+
redirect: z.any().optional(),
|
72
|
+
});
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Log Function Schema - Simple function type
|
76
|
+
*/
|
77
|
+
export const AppwriteLogSchema = z.any();
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Error Function Schema - Simple function type
|
81
|
+
*/
|
82
|
+
export const AppwriteErrorSchema = z.any();
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Complete Appwrite Context Schema
|
86
|
+
*/
|
87
|
+
export const AppwriteContextSchema = z.object({
|
88
|
+
req: AppwriteRequestSchema,
|
89
|
+
res: AppwriteResponseSchema,
|
90
|
+
log: AppwriteLogSchema,
|
91
|
+
error: AppwriteErrorSchema,
|
92
|
+
});
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Type inference helpers
|
96
|
+
*/
|
97
|
+
export type AppwriteHeaders = z.infer<typeof AppwriteHeadersSchema>;
|
98
|
+
export type AppwriteEnv = z.infer<typeof AppwriteEnvSchema>;
|
99
|
+
export type AppwriteRequest = z.infer<typeof AppwriteRequestSchema>;
|
100
|
+
export type AppwriteResponse = z.infer<typeof AppwriteResponseSchema>;
|
101
|
+
export type AppwriteLog = z.infer<typeof AppwriteLogSchema>;
|
102
|
+
export type AppwriteError = z.infer<typeof AppwriteErrorSchema>;
|
103
|
+
export type AppwriteContext = z.infer<typeof AppwriteContextSchema>;
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import { app } from "./app.js";
|
2
|
+
import { AppwriteContext, AppwriteContextSchema } from "./context.js";
|
3
|
+
import { convertAppwriteToWebRequest } from "./adapters/request.js";
|
4
|
+
import { convertWebResponseToAppwrite } from "./adapters/response.js";
|
5
|
+
import { appwriteMiddleware } from "./middleware/appwrite.js";
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Main Appwrite function entry point
|
9
|
+
* This function receives the Appwrite context and routes it through Hono
|
10
|
+
*/
|
11
|
+
export default async function (context: AppwriteContext) {
|
12
|
+
const { req, res, log, error } = context;
|
13
|
+
|
14
|
+
try {
|
15
|
+
// Optional: Validate the context using Zod schema
|
16
|
+
AppwriteContextSchema.parse(context);
|
17
|
+
log("Context validation successful");
|
18
|
+
|
19
|
+
// Add Appwrite context middleware to the Hono app
|
20
|
+
app.use("*", appwriteMiddleware(context));
|
21
|
+
|
22
|
+
// Convert Appwrite request to Web API Request for Hono
|
23
|
+
const webRequest = convertAppwriteToWebRequest(req);
|
24
|
+
|
25
|
+
log(`Processing ${req.method} request to ${req.path || "/"}`);
|
26
|
+
|
27
|
+
// Create execution environment for Hono
|
28
|
+
const env = {
|
29
|
+
// Add any environment variables or context needed by Hono
|
30
|
+
APPWRITE_FUNCTION_ENDPOINT: process.env["APPWRITE_FUNCTION_ENDPOINT"],
|
31
|
+
APPWRITE_FUNCTION_PROJECT_ID: process.env["APPWRITE_FUNCTION_PROJECT_ID"],
|
32
|
+
APPWRITE_FUNCTION_API_KEY: process.env["APPWRITE_FUNCTION_API_KEY"],
|
33
|
+
};
|
34
|
+
|
35
|
+
// Process request through Hono app
|
36
|
+
const honoResponse = await app.fetch(webRequest, env);
|
37
|
+
|
38
|
+
// Convert Hono response back to Appwrite response format
|
39
|
+
await convertWebResponseToAppwrite(honoResponse, res);
|
40
|
+
|
41
|
+
log(`Request completed with status ${honoResponse.status}`);
|
42
|
+
|
43
|
+
} catch (validationError) {
|
44
|
+
error(`Function execution failed: ${validationError}`);
|
45
|
+
|
46
|
+
// Return error response
|
47
|
+
return res.json({
|
48
|
+
error: "Function execution failed",
|
49
|
+
message: validationError instanceof Error ? validationError.message : String(validationError),
|
50
|
+
functionName: "{{functionName}}",
|
51
|
+
timestamp: new Date().toISOString(),
|
52
|
+
}, 500);
|
53
|
+
}
|
54
|
+
}
|
@@ -0,0 +1,119 @@
|
|
1
|
+
import type { MiddlewareHandler } from "hono";
|
2
|
+
import type { AppwriteContext } from "../context.js";
|
3
|
+
|
4
|
+
// Extend Hono's context to include Appwrite context
|
5
|
+
declare module "hono" {
|
6
|
+
interface ContextVariableMap {
|
7
|
+
appwriteContext: AppwriteContext;
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|
11
|
+
/**
|
12
|
+
* Middleware to inject Appwrite context into Hono
|
13
|
+
*/
|
14
|
+
export const appwriteMiddleware = (appwriteContext: AppwriteContext): MiddlewareHandler => {
|
15
|
+
return async (c, next) => {
|
16
|
+
// Inject Appwrite context into Hono context
|
17
|
+
c.set("appwriteContext", appwriteContext);
|
18
|
+
|
19
|
+
// Add logging helper methods to Hono context
|
20
|
+
c.log = (message: string) => {
|
21
|
+
appwriteContext.log(`[${c.req.method} ${c.req.path}] ${message}`);
|
22
|
+
};
|
23
|
+
|
24
|
+
c.error = (message: string) => {
|
25
|
+
appwriteContext.error(`[${c.req.method} ${c.req.path}] ${message}`);
|
26
|
+
};
|
27
|
+
|
28
|
+
await next();
|
29
|
+
};
|
30
|
+
};
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Middleware for request logging
|
34
|
+
*/
|
35
|
+
export const requestLogger = (): MiddlewareHandler => {
|
36
|
+
return async (c, next) => {
|
37
|
+
const start = Date.now();
|
38
|
+
const appwriteContext = c.get("appwriteContext");
|
39
|
+
|
40
|
+
appwriteContext?.log(`→ ${c.req.method} ${c.req.path}`);
|
41
|
+
|
42
|
+
await next();
|
43
|
+
|
44
|
+
const duration = Date.now() - start;
|
45
|
+
appwriteContext?.log(`← ${c.req.method} ${c.req.path} ${c.res.status} (${duration}ms)`);
|
46
|
+
};
|
47
|
+
};
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Middleware for error handling
|
51
|
+
*/
|
52
|
+
export const errorHandler = (): MiddlewareHandler => {
|
53
|
+
return async (c, next) => {
|
54
|
+
try {
|
55
|
+
await next();
|
56
|
+
} catch (err) {
|
57
|
+
const appwriteContext = c.get("appwriteContext");
|
58
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
59
|
+
|
60
|
+
appwriteContext?.error(`Error in ${c.req.method} ${c.req.path}: ${error.message}`);
|
61
|
+
|
62
|
+
return c.json(
|
63
|
+
{
|
64
|
+
error: "Internal Server Error",
|
65
|
+
message: error.message,
|
66
|
+
path: c.req.path,
|
67
|
+
method: c.req.method,
|
68
|
+
},
|
69
|
+
500
|
70
|
+
);
|
71
|
+
}
|
72
|
+
};
|
73
|
+
};
|
74
|
+
|
75
|
+
/**
|
76
|
+
* Middleware to extract and validate Appwrite headers
|
77
|
+
*/
|
78
|
+
export const appwriteHeaders = (): MiddlewareHandler => {
|
79
|
+
return async (c, next) => {
|
80
|
+
const appwriteContext = c.get("appwriteContext");
|
81
|
+
const headers = appwriteContext?.req.headers;
|
82
|
+
|
83
|
+
// Add helpful getters for common Appwrite headers
|
84
|
+
c.appwrite = {
|
85
|
+
trigger: headers?.["x-appwrite-trigger"] as "http" | "schedule" | "event" | undefined,
|
86
|
+
event: headers?.["x-appwrite-event"],
|
87
|
+
key: headers?.["x-appwrite-key"],
|
88
|
+
userId: headers?.["x-appwrite-user-id"],
|
89
|
+
userJwt: headers?.["x-appwrite-user-jwt"],
|
90
|
+
countryCode: headers?.["x-appwrite-country-code"],
|
91
|
+
continentCode: headers?.["x-appwrite-continent-code"],
|
92
|
+
continentEu: headers?.["x-appwrite-continent-eu"],
|
93
|
+
isUserAuthenticated: () => !!headers?.["x-appwrite-user-id"],
|
94
|
+
isApiKeyRequest: () => !!headers?.["x-appwrite-key"],
|
95
|
+
};
|
96
|
+
|
97
|
+
await next();
|
98
|
+
};
|
99
|
+
};
|
100
|
+
|
101
|
+
// Extend Hono context to include Appwrite helpers
|
102
|
+
declare module "hono" {
|
103
|
+
interface Context {
|
104
|
+
log: (message: string) => void;
|
105
|
+
error: (message: string) => void;
|
106
|
+
appwrite: {
|
107
|
+
trigger?: "http" | "schedule" | "event";
|
108
|
+
event?: string;
|
109
|
+
key?: string;
|
110
|
+
userId?: string;
|
111
|
+
userJwt?: string;
|
112
|
+
countryCode?: string;
|
113
|
+
continentCode?: string;
|
114
|
+
continentEu?: string;
|
115
|
+
isUserAuthenticated: () => boolean;
|
116
|
+
isApiKeyRequest: () => boolean;
|
117
|
+
};
|
118
|
+
}
|
119
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"target": "ES2022",
|
4
|
+
"module": "ES2022",
|
5
|
+
"moduleResolution": "node",
|
6
|
+
"allowSyntheticDefaultImports": true,
|
7
|
+
"esModuleInterop": true,
|
8
|
+
"allowJs": true,
|
9
|
+
"strict": true,
|
10
|
+
"skipLibCheck": true,
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
12
|
+
"declaration": true,
|
13
|
+
"outDir": "./dist",
|
14
|
+
"rootDir": "./src",
|
15
|
+
"resolveJsonModule": true,
|
16
|
+
"types": ["node"]
|
17
|
+
},
|
18
|
+
"include": ["src/**/*"],
|
19
|
+
"exclude": ["node_modules", "dist"]
|
20
|
+
}
|
@@ -0,0 +1,103 @@
|
|
1
|
+
import { z } from "zod";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Appwrite Request Headers Schema
|
5
|
+
*/
|
6
|
+
export const AppwriteHeadersSchema = z.object({
|
7
|
+
"x-appwrite-trigger": z.enum(["http", "schedule", "event"]).optional(),
|
8
|
+
"x-appwrite-event": z.string().optional(),
|
9
|
+
"x-appwrite-key": z.string().optional(),
|
10
|
+
"x-appwrite-user-id": z.string().optional(),
|
11
|
+
"x-appwrite-user-jwt": z.string().optional(),
|
12
|
+
"x-appwrite-country-code": z.string().optional(),
|
13
|
+
"x-appwrite-continent-code": z.string().optional(),
|
14
|
+
"x-appwrite-continent-eu": z.string().optional(),
|
15
|
+
}).catchall(z.string()); // Allow additional headers
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Appwrite Environment Variables Schema
|
19
|
+
*/
|
20
|
+
export const AppwriteEnvSchema = z.object({
|
21
|
+
APPWRITE_FUNCTION_API_ENDPOINT: z.string(),
|
22
|
+
APPWRITE_VERSION: z.string(),
|
23
|
+
APPWRITE_REGION: z.string(),
|
24
|
+
APPWRITE_FUNCTION_API_KEY: z.string().optional(),
|
25
|
+
APPWRITE_FUNCTION_ID: z.string(),
|
26
|
+
APPWRITE_FUNCTION_NAME: z.string(),
|
27
|
+
APPWRITE_FUNCTION_DEPLOYMENT: z.string(),
|
28
|
+
APPWRITE_FUNCTION_PROJECT_ID: z.string(),
|
29
|
+
APPWRITE_FUNCTION_RUNTIME_NAME: z.string(),
|
30
|
+
APPWRITE_FUNCTION_RUNTIME_VERSION: z.string(),
|
31
|
+
});
|
32
|
+
|
33
|
+
/**
|
34
|
+
* Appwrite Request Schema (full version from docs)
|
35
|
+
*/
|
36
|
+
export const AppwriteRequestSchema = z.object({
|
37
|
+
bodyText: z.string().optional(),
|
38
|
+
bodyJson: z.union([z.any(), z.string()]).transform((t) => {
|
39
|
+
if (typeof t === "string") {
|
40
|
+
try {
|
41
|
+
return JSON.parse(t);
|
42
|
+
} catch {
|
43
|
+
return null;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
return t;
|
47
|
+
}).optional(),
|
48
|
+
body: z.any().optional(),
|
49
|
+
headers: AppwriteHeadersSchema,
|
50
|
+
scheme: z.enum(["http", "https"]).optional(),
|
51
|
+
method: z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]),
|
52
|
+
url: z.string().optional(),
|
53
|
+
host: z.string().optional(),
|
54
|
+
port: z.number().or(z.string()).optional(),
|
55
|
+
path: z.string().optional(),
|
56
|
+
queryString: z.string().optional(),
|
57
|
+
query: z.record(z.string(), z.any()).optional(),
|
58
|
+
variables: z.record(z.string(), z.any()).optional(),
|
59
|
+
text: z.any().optional(),
|
60
|
+
payload: z.string().optional(),
|
61
|
+
});
|
62
|
+
|
63
|
+
/**
|
64
|
+
* Appwrite Response Schema
|
65
|
+
*/
|
66
|
+
export const AppwriteResponseSchema = z.object({
|
67
|
+
json: z.any(),
|
68
|
+
text: z.any(),
|
69
|
+
empty: z.any().optional(),
|
70
|
+
binary: z.any().optional(),
|
71
|
+
redirect: z.any().optional(),
|
72
|
+
});
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Log Function Schema - Simple function type
|
76
|
+
*/
|
77
|
+
export const AppwriteLogSchema = z.any();
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Error Function Schema - Simple function type
|
81
|
+
*/
|
82
|
+
export const AppwriteErrorSchema = z.any();
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Complete Appwrite Context Schema
|
86
|
+
*/
|
87
|
+
export const AppwriteContextSchema = z.object({
|
88
|
+
req: AppwriteRequestSchema,
|
89
|
+
res: AppwriteResponseSchema,
|
90
|
+
log: AppwriteLogSchema,
|
91
|
+
error: AppwriteErrorSchema,
|
92
|
+
});
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Type inference helpers
|
96
|
+
*/
|
97
|
+
export type AppwriteHeaders = z.infer<typeof AppwriteHeadersSchema>;
|
98
|
+
export type AppwriteEnv = z.infer<typeof AppwriteEnvSchema>;
|
99
|
+
export type AppwriteRequest = z.infer<typeof AppwriteRequestSchema>;
|
100
|
+
export type AppwriteResponse = z.infer<typeof AppwriteResponseSchema>;
|
101
|
+
export type AppwriteLog = z.infer<typeof AppwriteLogSchema>;
|
102
|
+
export type AppwriteError = z.infer<typeof AppwriteErrorSchema>;
|
103
|
+
export type AppwriteContext = z.infer<typeof AppwriteContextSchema>;
|
@@ -1,23 +1,29 @@
|
|
1
1
|
import { Client } from "node-appwrite";
|
2
|
-
import {
|
2
|
+
import { AppwriteContext, AppwriteContextSchema } from "./context.js";
|
3
|
+
|
4
|
+
export default async function (context: AppwriteContext) {
|
5
|
+
const { req, res, log, error } = context;
|
6
|
+
|
7
|
+
// Optional: Validate the context using Zod schema
|
8
|
+
try {
|
9
|
+
AppwriteContextSchema.parse(context);
|
10
|
+
log("Context validation successful");
|
11
|
+
} catch (validationError) {
|
12
|
+
error(`Context validation failed: ${validationError}`);
|
13
|
+
}
|
3
14
|
|
4
|
-
export default async function ({
|
5
|
-
req,
|
6
|
-
res,
|
7
|
-
log,
|
8
|
-
error,
|
9
|
-
}: {
|
10
|
-
req: AppwriteRequest;
|
11
|
-
res: AppwriteResponse;
|
12
|
-
log: (message: string) => void;
|
13
|
-
error: (message: string) => void;
|
14
|
-
}) {
|
15
15
|
const client = new Client()
|
16
16
|
.setEndpoint(process.env["APPWRITE_FUNCTION_ENDPOINT"]!)
|
17
17
|
.setProject(process.env["APPWRITE_FUNCTION_PROJECT_ID"]!)
|
18
18
|
.setKey(req.headers["x-appwrite-key"] || "");
|
19
19
|
|
20
|
+
log(`Processing ${req.method} request to ${req.path}`);
|
21
|
+
|
20
22
|
return res.json({
|
21
23
|
message: "Hello from TypeScript function!",
|
24
|
+
functionName: "{{functionName}}",
|
25
|
+
method: req.method,
|
26
|
+
path: req.path,
|
27
|
+
headers: req.headers,
|
22
28
|
});
|
23
29
|
}
|
@@ -0,0 +1,125 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
from typing import Any, Literal, Protocol
|
3
|
+
|
4
|
+
from pydantic import BaseModel, Field
|
5
|
+
|
6
|
+
|
7
|
+
class AppwriteHeaders(BaseModel):
|
8
|
+
"""Appwrite request headers"""
|
9
|
+
|
10
|
+
x_appwrite_trigger: Literal["http", "schedule", "event"] | None = Field(
|
11
|
+
None, alias="x-appwrite-trigger"
|
12
|
+
)
|
13
|
+
x_appwrite_event: str | None = Field(None, alias="x-appwrite-event")
|
14
|
+
x_appwrite_key: str | None = Field(None, alias="x-appwrite-key")
|
15
|
+
x_appwrite_user_id: str | None = Field(None, alias="x-appwrite-user-id")
|
16
|
+
x_appwrite_user_jwt: str | None = Field(None, alias="x-appwrite-user-jwt")
|
17
|
+
x_appwrite_country_code: str | None = Field(None, alias="x-appwrite-country-code")
|
18
|
+
x_appwrite_continent_code: str | None = Field(
|
19
|
+
None, alias="x-appwrite-continent-code"
|
20
|
+
)
|
21
|
+
x_appwrite_continent_eu: str | None = Field(None, alias="x-appwrite-continent-eu")
|
22
|
+
|
23
|
+
# Allow additional headers
|
24
|
+
model_config = {"extra": "allow", "populate_by_name": True}
|
25
|
+
|
26
|
+
|
27
|
+
class AppwriteEnv(BaseModel):
|
28
|
+
"""Appwrite environment variables"""
|
29
|
+
|
30
|
+
APPWRITE_FUNCTION_API_ENDPOINT: str
|
31
|
+
APPWRITE_VERSION: str
|
32
|
+
APPWRITE_REGION: str
|
33
|
+
APPWRITE_FUNCTION_API_KEY: str | None = None
|
34
|
+
APPWRITE_FUNCTION_ID: str
|
35
|
+
APPWRITE_FUNCTION_NAME: str
|
36
|
+
APPWRITE_FUNCTION_DEPLOYMENT: str
|
37
|
+
APPWRITE_FUNCTION_PROJECT_ID: str
|
38
|
+
APPWRITE_FUNCTION_RUNTIME_NAME: str
|
39
|
+
APPWRITE_FUNCTION_RUNTIME_VERSION: str
|
40
|
+
|
41
|
+
|
42
|
+
class AppwriteRequest(Protocol):
|
43
|
+
"""Appwrite function request object with proper callable typing"""
|
44
|
+
|
45
|
+
bodyText: str | None
|
46
|
+
bodyJson: dict[str, Any] | str | None
|
47
|
+
body: Any
|
48
|
+
headers: dict[str, str]
|
49
|
+
scheme: Literal["http", "https"] | None
|
50
|
+
method: Literal["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]
|
51
|
+
url: str | None
|
52
|
+
host: str | None
|
53
|
+
port: int | str | None
|
54
|
+
path: str | None
|
55
|
+
queryString: str | None
|
56
|
+
query: dict[str, str | list[str]] | None
|
57
|
+
variables: dict[str, str] | None
|
58
|
+
payload: str | None
|
59
|
+
|
60
|
+
async def text(self) -> str: ...
|
61
|
+
|
62
|
+
|
63
|
+
class AppwriteResponse(Protocol):
|
64
|
+
"""Appwrite function response object with proper callable typing"""
|
65
|
+
|
66
|
+
def json(
|
67
|
+
self,
|
68
|
+
data: Any,
|
69
|
+
status: int | None = None,
|
70
|
+
headers: dict[str, str] | None = None,
|
71
|
+
) -> None: ...
|
72
|
+
|
73
|
+
def text(
|
74
|
+
self,
|
75
|
+
body: str,
|
76
|
+
status: int | None = None,
|
77
|
+
headers: dict[str, str] | None = None,
|
78
|
+
) -> None: ...
|
79
|
+
|
80
|
+
def empty(self) -> None: ...
|
81
|
+
|
82
|
+
def binary(self, data: bytes) -> None: ...
|
83
|
+
|
84
|
+
def redirect(self, url: str, status: int | None = None) -> None: ...
|
85
|
+
|
86
|
+
|
87
|
+
class AppwriteContext(Protocol):
|
88
|
+
"""Complete Appwrite function context with proper typing"""
|
89
|
+
|
90
|
+
req: AppwriteRequest
|
91
|
+
res: AppwriteResponse
|
92
|
+
|
93
|
+
def log(self, message: str) -> None: ...
|
94
|
+
|
95
|
+
def error(self, message: str) -> None: ...
|
96
|
+
|
97
|
+
|
98
|
+
# Alternative: BaseModel version for cases where you need Pydantic features
|
99
|
+
# but want proper typing hints
|
100
|
+
|
101
|
+
|
102
|
+
class AppwriteRequestModel(BaseModel):
|
103
|
+
"""Pydantic model for request validation (if needed)"""
|
104
|
+
|
105
|
+
bodyText: str | None = None
|
106
|
+
bodyJson: dict[str, Any] | str | None = None
|
107
|
+
body: Any = None
|
108
|
+
headers: dict[str, str] = Field(default_factory=dict)
|
109
|
+
scheme: Literal["http", "https"] | None = None
|
110
|
+
method: Literal["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]
|
111
|
+
url: str | None = None
|
112
|
+
host: str | None = None
|
113
|
+
port: int | str | None = None
|
114
|
+
path: str | None = None
|
115
|
+
queryString: str | None = None
|
116
|
+
query: dict[str, str | list[str]] | None = None
|
117
|
+
variables: dict[str, str] | None = None
|
118
|
+
payload: str | None = None
|
119
|
+
|
120
|
+
model_config = {"arbitrary_types_allowed": True}
|
121
|
+
|
122
|
+
|
123
|
+
# Type alias for the log/error functions
|
124
|
+
LogFunction = Callable[[str], None]
|
125
|
+
ErrorFunction = Callable[[str], None]
|
@@ -1,16 +1,46 @@
|
|
1
|
+
import os
|
1
2
|
from appwrite.client import Client
|
3
|
+
from context import AppwriteContext, AppwriteHeaders, AppwriteRequestModel
|
2
4
|
|
3
|
-
def main(context):
|
5
|
+
def main(context: AppwriteContext):
|
4
6
|
req = context.req
|
5
7
|
res = context.res
|
6
8
|
log = context.log
|
7
9
|
error = context.error
|
8
10
|
|
11
|
+
# Optional: Validate request headers using Pydantic
|
12
|
+
try:
|
13
|
+
headers = AppwriteHeaders.model_validate(req.headers)
|
14
|
+
log("Headers validation successful")
|
15
|
+
except Exception as validation_error:
|
16
|
+
error(f"Headers validation failed: {validation_error}")
|
17
|
+
|
18
|
+
# Optional: Validate full request using Pydantic model
|
19
|
+
try:
|
20
|
+
request_model = AppwriteRequestModel.model_validate({
|
21
|
+
'method': req.method,
|
22
|
+
'headers': req.headers,
|
23
|
+
'path': req.path,
|
24
|
+
'url': req.url,
|
25
|
+
'body': req.body,
|
26
|
+
'bodyText': req.bodyText,
|
27
|
+
'bodyJson': req.bodyJson
|
28
|
+
})
|
29
|
+
log("Request validation successful")
|
30
|
+
except Exception as validation_error:
|
31
|
+
error(f"Request validation failed: {validation_error}")
|
32
|
+
|
9
33
|
client = Client()
|
10
|
-
client.set_endpoint(
|
11
|
-
client.set_project(
|
12
|
-
client.set_key(
|
34
|
+
client.set_endpoint(os.getenv('APPWRITE_FUNCTION_ENDPOINT'))
|
35
|
+
client.set_project(os.getenv('APPWRITE_FUNCTION_PROJECT_ID'))
|
36
|
+
client.set_key(os.getenv('APPWRITE_FUNCTION_API_KEY'))
|
37
|
+
|
38
|
+
log(f"Processing {req.method} request to {req.path}")
|
13
39
|
|
14
40
|
return res.json({
|
15
|
-
'message': 'Hello from Python function!'
|
41
|
+
'message': 'Hello from Python function!',
|
42
|
+
'functionName': '{{functionName}}',
|
43
|
+
'method': req.method,
|
44
|
+
'path': req.path,
|
45
|
+
'headers': req.headers
|
16
46
|
})
|