appwrite-utils-cli 0.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/README.md +80 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +74 -0
- package/dist/migrations/afterImportActions.d.ts +12 -0
- package/dist/migrations/afterImportActions.js +196 -0
- package/dist/migrations/attributes.d.ts +4 -0
- package/dist/migrations/attributes.js +158 -0
- package/dist/migrations/backup.d.ts +621 -0
- package/dist/migrations/backup.js +159 -0
- package/dist/migrations/collections.d.ts +16 -0
- package/dist/migrations/collections.js +207 -0
- package/dist/migrations/converters.d.ts +179 -0
- package/dist/migrations/converters.js +575 -0
- package/dist/migrations/dbHelpers.d.ts +5 -0
- package/dist/migrations/dbHelpers.js +54 -0
- package/dist/migrations/importController.d.ts +44 -0
- package/dist/migrations/importController.js +312 -0
- package/dist/migrations/importDataActions.d.ts +44 -0
- package/dist/migrations/importDataActions.js +219 -0
- package/dist/migrations/indexes.d.ts +4 -0
- package/dist/migrations/indexes.js +18 -0
- package/dist/migrations/logging.d.ts +2 -0
- package/dist/migrations/logging.js +14 -0
- package/dist/migrations/migrationHelper.d.ts +18 -0
- package/dist/migrations/migrationHelper.js +66 -0
- package/dist/migrations/queue.d.ts +13 -0
- package/dist/migrations/queue.js +79 -0
- package/dist/migrations/relationships.d.ts +90 -0
- package/dist/migrations/relationships.js +209 -0
- package/dist/migrations/schema.d.ts +3142 -0
- package/dist/migrations/schema.js +485 -0
- package/dist/migrations/schemaStrings.d.ts +12 -0
- package/dist/migrations/schemaStrings.js +261 -0
- package/dist/migrations/setupDatabase.d.ts +7 -0
- package/dist/migrations/setupDatabase.js +151 -0
- package/dist/migrations/storage.d.ts +8 -0
- package/dist/migrations/storage.js +241 -0
- package/dist/migrations/users.d.ts +11 -0
- package/dist/migrations/users.js +114 -0
- package/dist/migrations/validationRules.d.ts +43 -0
- package/dist/migrations/validationRules.js +42 -0
- package/dist/schemas/authUser.d.ts +62 -0
- package/dist/schemas/authUser.js +17 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.js +5 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.js +5 -0
- package/dist/utils/configSchema.json +742 -0
- package/dist/utils/helperFunctions.d.ts +34 -0
- package/dist/utils/helperFunctions.js +72 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/setupFiles.d.ts +2 -0
- package/dist/utils/setupFiles.js +276 -0
- package/dist/utilsController.d.ts +30 -0
- package/dist/utilsController.js +106 -0
- package/package.json +34 -0
- package/src/main.ts +77 -0
- package/src/migrations/afterImportActions.ts +300 -0
- package/src/migrations/attributes.ts +315 -0
- package/src/migrations/backup.ts +189 -0
- package/src/migrations/collections.ts +303 -0
- package/src/migrations/converters.ts +628 -0
- package/src/migrations/dbHelpers.ts +89 -0
- package/src/migrations/importController.ts +509 -0
- package/src/migrations/importDataActions.ts +313 -0
- package/src/migrations/indexes.ts +37 -0
- package/src/migrations/logging.ts +15 -0
- package/src/migrations/migrationHelper.ts +100 -0
- package/src/migrations/queue.ts +119 -0
- package/src/migrations/relationships.ts +336 -0
- package/src/migrations/schema.ts +590 -0
- package/src/migrations/schemaStrings.ts +310 -0
- package/src/migrations/setupDatabase.ts +219 -0
- package/src/migrations/storage.ts +351 -0
- package/src/migrations/users.ts +148 -0
- package/src/migrations/validationRules.ts +63 -0
- package/src/schemas/authUser.ts +23 -0
- package/src/setup.ts +8 -0
- package/src/types.ts +14 -0
- package/src/utils/configSchema.json +742 -0
- package/src/utils/helperFunctions.ts +111 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/setupFiles.ts +295 -0
- package/src/utilsController.ts +173 -0
- package/tsconfig.json +37 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { AppwriteConfig } from "../types.js";
|
|
2
|
+
import type { Models, Storage } from "node-appwrite";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
export const toPascalCase = (str: string): string => {
|
|
7
|
+
return (
|
|
8
|
+
str
|
|
9
|
+
// Split the string into words on spaces or camelCase transitions
|
|
10
|
+
.split(/(?:\s+)|(?:([A-Z][a-z]+))/g)
|
|
11
|
+
// Filter out empty strings that can appear due to the split regex
|
|
12
|
+
.filter(Boolean)
|
|
13
|
+
// Capitalize the first letter of each word and join them together
|
|
14
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
15
|
+
.join("")
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const toCamelCase = (str: string): string => {
|
|
20
|
+
return str
|
|
21
|
+
.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) =>
|
|
22
|
+
index === 0 ? word.toLowerCase() : word.toUpperCase()
|
|
23
|
+
)
|
|
24
|
+
.replace(/\s+/g, "");
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const ensureDirectoryExistence = (filePath: string) => {
|
|
28
|
+
const dirname = path.dirname(filePath);
|
|
29
|
+
if (fs.existsSync(dirname)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
ensureDirectoryExistence(dirname);
|
|
33
|
+
fs.mkdirSync(dirname);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const writeFileSync = (
|
|
37
|
+
filePath: string,
|
|
38
|
+
content: string,
|
|
39
|
+
options: { flag: string }
|
|
40
|
+
) => {
|
|
41
|
+
ensureDirectoryExistence(filePath);
|
|
42
|
+
fs.writeFileSync(filePath, content, options);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const readFileSync = (filePath: string) => {
|
|
46
|
+
return fs.readFileSync(filePath, "utf8");
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const existsSync = (filePath: string) => {
|
|
50
|
+
return fs.existsSync(filePath);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const mkdirSync = (filePath: string) => {
|
|
54
|
+
ensureDirectoryExistence(filePath);
|
|
55
|
+
fs.mkdirSync(filePath);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const readdirSync = (filePath: string) => {
|
|
59
|
+
return fs.readdirSync(filePath);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const areCollectionNamesSame = (a: string, b: string) => {
|
|
63
|
+
return (
|
|
64
|
+
a.toLowerCase().trim().replace(" ", "") ===
|
|
65
|
+
b.toLowerCase().trim().replace(" ", "")
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Generates the view URL for a specific file based on the provided endpoint, project ID, bucket ID, file ID, and optional JWT token.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} endpoint - the base URL endpoint
|
|
73
|
+
* @param {string} projectId - the ID of the project
|
|
74
|
+
* @param {string} bucketId - the ID of the bucket
|
|
75
|
+
* @param {string} fileId - the ID of the file
|
|
76
|
+
* @param {Models.Jwt} [jwt] - optional JWT token generated via the Appwrite SDK
|
|
77
|
+
* @return {string} the generated view URL for the file
|
|
78
|
+
*/
|
|
79
|
+
export const getFileViewUrl = (
|
|
80
|
+
endpoint: string,
|
|
81
|
+
projectId: string,
|
|
82
|
+
bucketId: string,
|
|
83
|
+
fileId: string,
|
|
84
|
+
jwt?: Models.Jwt
|
|
85
|
+
) => {
|
|
86
|
+
return `${endpoint}/storage/buckets/${bucketId}/files/${fileId}/view?project=${projectId}${
|
|
87
|
+
jwt ? `&jwt=${jwt.jwt}` : ""
|
|
88
|
+
}`;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Generates a download URL for a file based on the provided endpoint, project ID, bucket ID, file ID, and optionally a JWT.
|
|
93
|
+
*
|
|
94
|
+
* @param {string} endpoint - The base URL endpoint.
|
|
95
|
+
* @param {string} projectId - The ID of the project.
|
|
96
|
+
* @param {string} bucketId - The ID of the bucket.
|
|
97
|
+
* @param {string} fileId - The ID of the file.
|
|
98
|
+
* @param {Models.Jwt} [jwt] - Optional JWT object for authentication with Appwrite.
|
|
99
|
+
* @return {string} The complete download URL for the file.
|
|
100
|
+
*/
|
|
101
|
+
export const getFileDownloadUrl = (
|
|
102
|
+
endpoint: string,
|
|
103
|
+
projectId: string,
|
|
104
|
+
bucketId: string,
|
|
105
|
+
fileId: string,
|
|
106
|
+
jwt?: Models.Jwt
|
|
107
|
+
) => {
|
|
108
|
+
return `${endpoint}/storage/buckets/${bucketId}/files/${fileId}/download?project=${projectId}${
|
|
109
|
+
jwt ? `&jwt=${jwt.jwt}` : ""
|
|
110
|
+
}`;
|
|
111
|
+
};
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync, existsSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import configSchema from "./configSchema.json" assert { type: "json" };
|
|
4
|
+
|
|
5
|
+
// Define our YAML files
|
|
6
|
+
// Define our YAML files
|
|
7
|
+
const configFileExample = `# yaml-language-server: $schema=./.appwrite/appwriteUtilsConfigSchema.json
|
|
8
|
+
# Appwrite configuration settings
|
|
9
|
+
appwriteEndpoint: 'https://cloud.appwrite.io/v1' # Your Appwrite endpoint. Default: 'https://cloud.appwrite.io/v1'
|
|
10
|
+
appwriteProject: 'YOUR_PROJECT_ID' # Your Appwrite project ID
|
|
11
|
+
appwriteKey: 'YOUR_API_KEY' # Your Appwrite API key (needs storage and databases at minimum)
|
|
12
|
+
appwriteClient: null # Your Appwrite client -- don't worry about this
|
|
13
|
+
enableDevDatabase: true # Enable development database alongside main
|
|
14
|
+
enableBackups: true # Enable backups
|
|
15
|
+
backupInterval: 3600 # Backup interval in seconds
|
|
16
|
+
backupRetention: 30 # Backup retention in days
|
|
17
|
+
enableBackupCleanup: true # Enable backup cleanup
|
|
18
|
+
enableMockData: false # Enable mock data generation
|
|
19
|
+
enableWipeOtherDatabases: true # Enable wiping other databases
|
|
20
|
+
documentBucketId: 'documents' # Your Appwrite bucket ID for documents
|
|
21
|
+
usersCollectionName: 'Members' # Your Appwrite collection for any extra info while importing members (if any)
|
|
22
|
+
# This allows you to use any targetKey in the users field to create your user
|
|
23
|
+
# these are: name, email, phone, labels, prefs, password, userId and (not yet added)
|
|
24
|
+
# $createdAt, $updatedAt -- Add them to your attributeMappings (NOT attributes) to define them and set the
|
|
25
|
+
# targetKey to the same as the Appwrite targetKey
|
|
26
|
+
databases:
|
|
27
|
+
- $id: 'main'
|
|
28
|
+
name: 'Main'
|
|
29
|
+
- $id: 'staging'
|
|
30
|
+
name: 'Staging'
|
|
31
|
+
- $id: 'dev'
|
|
32
|
+
name: 'Development'
|
|
33
|
+
collections:
|
|
34
|
+
- name: 'Members'
|
|
35
|
+
$permissions:
|
|
36
|
+
- permission: read
|
|
37
|
+
target: any
|
|
38
|
+
- permission: create
|
|
39
|
+
target: users
|
|
40
|
+
- permission: update
|
|
41
|
+
target: users
|
|
42
|
+
- permission: delete
|
|
43
|
+
target: users
|
|
44
|
+
attributes:
|
|
45
|
+
- key: 'idOrig'
|
|
46
|
+
type: 'string'
|
|
47
|
+
size: 255
|
|
48
|
+
required: false
|
|
49
|
+
- key: 'dogs'
|
|
50
|
+
type: 'relationship'
|
|
51
|
+
relatedCollection: 'Dogs'
|
|
52
|
+
relationType: 'oneToMany'
|
|
53
|
+
twoWay: true
|
|
54
|
+
twoWayKey: 'owner'
|
|
55
|
+
side: 'parent'
|
|
56
|
+
onDelete: 'cascade'
|
|
57
|
+
importMapping: { originalIdField: 'idOrig', targetField: 'ownerIdOrig' }
|
|
58
|
+
- key: 'dogIds'
|
|
59
|
+
type: 'string'
|
|
60
|
+
size: 255
|
|
61
|
+
array: true
|
|
62
|
+
- key: 'profilePhoto'
|
|
63
|
+
type: 'string'
|
|
64
|
+
size: 255
|
|
65
|
+
required: false
|
|
66
|
+
- key: 'profilePhotoTest'
|
|
67
|
+
type: 'string'
|
|
68
|
+
size: 255
|
|
69
|
+
required: false
|
|
70
|
+
indexes:
|
|
71
|
+
- key: 'idOrig_index'
|
|
72
|
+
type: 'key'
|
|
73
|
+
attributes: ['idOrig']
|
|
74
|
+
importDefs:
|
|
75
|
+
- filePath: 'importData/members.json'
|
|
76
|
+
basePath: 'RECORDS'
|
|
77
|
+
attributeMappings:
|
|
78
|
+
- oldKey: 'id'
|
|
79
|
+
targetKey: 'idOrig'
|
|
80
|
+
converters: ['anyToString']
|
|
81
|
+
postImportActions:
|
|
82
|
+
- action: 'checkAndUpdateFieldInDocument'
|
|
83
|
+
params:
|
|
84
|
+
- "{dbId}"
|
|
85
|
+
- "{collId}"
|
|
86
|
+
- "{docId}"
|
|
87
|
+
- "idOrig"
|
|
88
|
+
- "{id}"
|
|
89
|
+
- "{$id}"
|
|
90
|
+
- oldKey: 'name'
|
|
91
|
+
targetKey: 'name'
|
|
92
|
+
- oldKey: 'email'
|
|
93
|
+
targetKey: 'email'
|
|
94
|
+
- oldKey: 'doesntMatter'
|
|
95
|
+
targetKey: 'profilePhoto'
|
|
96
|
+
fileData: { name: "profilePhoto_{id}", path: "importData/profilePhotos" }
|
|
97
|
+
- oldKey: 'photoUrl'
|
|
98
|
+
targetKey: 'profilePhotoTest'
|
|
99
|
+
fileData: { name: "profilePhotoTest_{id}", path: "{photoUrl}" }
|
|
100
|
+
- name: 'Dogs'
|
|
101
|
+
$permissions:
|
|
102
|
+
- permission: read
|
|
103
|
+
target: any
|
|
104
|
+
- permission: create
|
|
105
|
+
target: users
|
|
106
|
+
- permission: update
|
|
107
|
+
target: users
|
|
108
|
+
- permission: delete
|
|
109
|
+
target: users
|
|
110
|
+
attributes:
|
|
111
|
+
- key: 'name'
|
|
112
|
+
type: 'string'
|
|
113
|
+
size: 255
|
|
114
|
+
required: true
|
|
115
|
+
- key: 'breed'
|
|
116
|
+
type: 'string'
|
|
117
|
+
size: 255
|
|
118
|
+
required: false
|
|
119
|
+
- key: 'age'
|
|
120
|
+
type: 'integer'
|
|
121
|
+
required: false
|
|
122
|
+
min: 0
|
|
123
|
+
max: 100
|
|
124
|
+
- key: 'idOrig'
|
|
125
|
+
type: 'string'
|
|
126
|
+
size: 20
|
|
127
|
+
required: false
|
|
128
|
+
- key: 'ownerIdOrig'
|
|
129
|
+
type: 'string'
|
|
130
|
+
size: 255
|
|
131
|
+
required: false
|
|
132
|
+
- key: 'vetRecords'
|
|
133
|
+
type: 'string'
|
|
134
|
+
size: 255
|
|
135
|
+
required: false
|
|
136
|
+
- key: 'vetRecordIds'
|
|
137
|
+
type: 'string'
|
|
138
|
+
size: 255
|
|
139
|
+
array: true
|
|
140
|
+
required: false
|
|
141
|
+
indexes:
|
|
142
|
+
- key: 'ownerIdIndex'
|
|
143
|
+
type: 'key'
|
|
144
|
+
attributes: ['ownerIdOrig']
|
|
145
|
+
importDefs:
|
|
146
|
+
- filePath: 'importData/dogs.json'
|
|
147
|
+
basePath: 'RECORDS'
|
|
148
|
+
attributeMappings:
|
|
149
|
+
- oldKey: 'id'
|
|
150
|
+
targetKey: 'idOrig'
|
|
151
|
+
- oldKey: 'name'
|
|
152
|
+
targetKey: 'name'
|
|
153
|
+
- oldKey: 'breed'
|
|
154
|
+
targetKey: 'breed'
|
|
155
|
+
- oldKey: 'age'
|
|
156
|
+
targetKey: 'age'
|
|
157
|
+
- oldKey: 'ownerId'
|
|
158
|
+
targetKey: 'ownerIdOrig'
|
|
159
|
+
- oldKey: 'vetRecords'
|
|
160
|
+
targetKey: 'vetRecords'
|
|
161
|
+
converters: ['stringifyObject']
|
|
162
|
+
- oldKey: 'vetRecords.[any].id'
|
|
163
|
+
targetKey: 'vetRecordIds'
|
|
164
|
+
converters: ['anyToString']
|
|
165
|
+
- filePath: 'importData/dogs.json'
|
|
166
|
+
basePath: 'RECORDS'
|
|
167
|
+
type: 'update'
|
|
168
|
+
updateMapping: { originalIdField: 'id', targetField: 'idOrig' }
|
|
169
|
+
attributeMappings:
|
|
170
|
+
- oldKey: 'name'
|
|
171
|
+
targetKey: 'name'
|
|
172
|
+
- oldKey: 'breed'
|
|
173
|
+
targetKey: 'breed'
|
|
174
|
+
- oldKey: 'age'
|
|
175
|
+
targetKey: 'age'`;
|
|
176
|
+
|
|
177
|
+
const configFile = `# yaml-language-server: $schema=./.appwrite/appwriteUtilsConfigSchema.json
|
|
178
|
+
# Basic Appwrite configuration settings
|
|
179
|
+
appwriteEndpoint: 'https://cloud.appwrite.io/v1' # Your Appwrite endpoint
|
|
180
|
+
appwriteProject: 'YOUR_PROJECT_ID' # Your Appwrite project ID
|
|
181
|
+
appwriteKey: 'YOUR_API_KEY' # Your Appwrite API key (needs storage and databases at minimum)
|
|
182
|
+
enableDevDatabase: true # Enable development database alongside main. Default: true
|
|
183
|
+
enableBackups: true # Enable backups. Default: true
|
|
184
|
+
backupInterval: 3600 # Backup interval in seconds. Default: 3600 - DOES NOTHING RIGHT NOW
|
|
185
|
+
backupRetention: 30 # Backup retention in days. Default: 30 - DOES NOTHING RIGHT NOW
|
|
186
|
+
enableBackupCleanup: true # Enable backup cleanup. Default: true - DOES NOTHING RIGHT NOW
|
|
187
|
+
enableMockData: false # Enable mock data generation. Default: false - DOES NOTHING RIGHT NOW
|
|
188
|
+
enableWipeOtherDatabases: true # Enable wiping other databases. Default: true
|
|
189
|
+
documentBucketId: 'documents' # Your Appwrite bucket ID for documents. Default: 'documents'
|
|
190
|
+
usersCollectionName: 'Members' # Your Appwrite collection for any extra info while importing members (if any). Default: 'Members'
|
|
191
|
+
# Databases configuration
|
|
192
|
+
# The first one is *always* Production
|
|
193
|
+
# The second is *always* Staging
|
|
194
|
+
# The third is *always* Development
|
|
195
|
+
# They are found by name matching (without spaces and all lowercase), not $id
|
|
196
|
+
# If no $id is included for anything defined, Appwrite will auto-generate one in its stead
|
|
197
|
+
databases:
|
|
198
|
+
- $id: 'main' # Database ID
|
|
199
|
+
name: 'Main' # Database name
|
|
200
|
+
- $id: 'staging'
|
|
201
|
+
name: 'Staging'
|
|
202
|
+
- $id: 'dev'
|
|
203
|
+
name: 'Development'
|
|
204
|
+
|
|
205
|
+
# Collections configuration
|
|
206
|
+
collections:
|
|
207
|
+
- name: 'ExampleCollection' # Collection name
|
|
208
|
+
$permissions: # Permissions for the collection
|
|
209
|
+
- permission: read # Permission type
|
|
210
|
+
target: any # Permission target
|
|
211
|
+
- permission: create
|
|
212
|
+
target: users
|
|
213
|
+
- permission: update
|
|
214
|
+
target: users
|
|
215
|
+
- permission: delete
|
|
216
|
+
target: users
|
|
217
|
+
attributes: # Attributes of the collection
|
|
218
|
+
- key: 'exampleKey' # Attribute key
|
|
219
|
+
type: 'string' # Attribute type
|
|
220
|
+
size: 255 # Size of the attribute (for strings)
|
|
221
|
+
required: true # Whether the attribute is required`;
|
|
222
|
+
|
|
223
|
+
export const customDefinitionsFile = `import type { ConverterFunctions, ValidationRules, AfterImportActions } from "appwrite-utils";
|
|
224
|
+
|
|
225
|
+
export const customConverterFunctions: ConverterFunctions = {
|
|
226
|
+
// Add your custom converter functions here
|
|
227
|
+
}
|
|
228
|
+
export const customValidationRules: ValidationRules = {
|
|
229
|
+
// Add your custom validation rules here
|
|
230
|
+
}
|
|
231
|
+
export const customAfterImportActions: AfterImportActions = {
|
|
232
|
+
// Add your custom after import actions here
|
|
233
|
+
}`;
|
|
234
|
+
|
|
235
|
+
export const setupDirsFiles = async (example: boolean = false) => {
|
|
236
|
+
const basePath = process.cwd();
|
|
237
|
+
const srcPath = path.join(basePath, "src");
|
|
238
|
+
|
|
239
|
+
// Check if src directory exists in the current working directory
|
|
240
|
+
if (!existsSync(srcPath)) {
|
|
241
|
+
console.error("No 'src' directory found in the current working directory.");
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const appwriteFolder = path.join(srcPath, "appwrite");
|
|
246
|
+
const appwriteConfigFile = path.join(appwriteFolder, "appwriteConfig.yaml");
|
|
247
|
+
const appwriteCustomDefsFile = path.join(
|
|
248
|
+
appwriteFolder,
|
|
249
|
+
"customDefinitions.ts"
|
|
250
|
+
);
|
|
251
|
+
// const appwriteMigrationsFolder = path.join(appwriteFolder, "migrations");
|
|
252
|
+
const appwriteSchemaFolder = path.join(appwriteFolder, "schemas");
|
|
253
|
+
const appwriteDataFolder = path.join(appwriteFolder, "importData");
|
|
254
|
+
const appwriteHiddenFolder = path.join(appwriteFolder, ".appwrite");
|
|
255
|
+
|
|
256
|
+
// Directory creation and file writing logic remains the same
|
|
257
|
+
if (!existsSync(appwriteFolder)) {
|
|
258
|
+
mkdirSync(appwriteFolder, { recursive: true });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!existsSync(appwriteConfigFile)) {
|
|
262
|
+
if (example) {
|
|
263
|
+
writeFileSync(appwriteConfigFile, configFileExample);
|
|
264
|
+
} else {
|
|
265
|
+
writeFileSync(appwriteConfigFile, configFile);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (!existsSync(appwriteCustomDefsFile)) {
|
|
270
|
+
writeFileSync(appwriteCustomDefsFile, customDefinitionsFile);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// if (!existsSync(appwriteMigrationsFolder)) {
|
|
274
|
+
// mkdirSync(appwriteMigrationsFolder, { recursive: true });
|
|
275
|
+
// }
|
|
276
|
+
|
|
277
|
+
if (!existsSync(appwriteSchemaFolder)) {
|
|
278
|
+
mkdirSync(appwriteSchemaFolder, { recursive: true });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (!existsSync(appwriteDataFolder)) {
|
|
282
|
+
mkdirSync(appwriteDataFolder, { recursive: true });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (!existsSync(appwriteHiddenFolder)) {
|
|
286
|
+
mkdirSync(appwriteHiddenFolder, { recursive: true });
|
|
287
|
+
}
|
|
288
|
+
const schemaFilePath = path.join(
|
|
289
|
+
appwriteHiddenFolder,
|
|
290
|
+
"appwriteUtilsConfigSchema.json"
|
|
291
|
+
);
|
|
292
|
+
writeFileSync(schemaFilePath, JSON.stringify(configSchema, undefined, 2));
|
|
293
|
+
|
|
294
|
+
console.log("Created config and setup files/directories successfully.");
|
|
295
|
+
};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { Client, Databases, Storage } from "node-appwrite";
|
|
2
|
+
import { startSetup } from "./migrations/setupDatabase.js";
|
|
3
|
+
import {
|
|
4
|
+
type AppwriteConfig,
|
|
5
|
+
AppwriteConfigSchema,
|
|
6
|
+
} from "./migrations/schema.js";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import { load } from "js-yaml";
|
|
10
|
+
import { ImportDataActions } from "./migrations/importDataActions.js";
|
|
11
|
+
import {
|
|
12
|
+
converterFunctions,
|
|
13
|
+
type ConverterFunctions,
|
|
14
|
+
} from "./migrations/converters.js";
|
|
15
|
+
import { readFileSync } from "./utils/helperFunctions.js";
|
|
16
|
+
import {
|
|
17
|
+
afterImportActions,
|
|
18
|
+
type AfterImportActions,
|
|
19
|
+
} from "./migrations/afterImportActions.js";
|
|
20
|
+
import {
|
|
21
|
+
validationRules,
|
|
22
|
+
type ValidationRules,
|
|
23
|
+
} from "./migrations/validationRules.js";
|
|
24
|
+
import { ImportController } from "./migrations/importController.js";
|
|
25
|
+
import _ from "lodash";
|
|
26
|
+
|
|
27
|
+
async function loadConfig(configPath: string) {
|
|
28
|
+
if (!fs.existsSync(configPath)) {
|
|
29
|
+
throw new Error(`Configuration file not found at ${configPath}`);
|
|
30
|
+
}
|
|
31
|
+
const configModule = await load(readFileSync(configPath), {
|
|
32
|
+
json: true,
|
|
33
|
+
});
|
|
34
|
+
return AppwriteConfigSchema.parse(configModule);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SetupOptions {
|
|
38
|
+
runProd: boolean;
|
|
39
|
+
runStaging: boolean;
|
|
40
|
+
runDev: boolean;
|
|
41
|
+
doBackup: boolean;
|
|
42
|
+
wipeDatabases: boolean;
|
|
43
|
+
wipeDocumentStorage: boolean;
|
|
44
|
+
wipeUsers: boolean;
|
|
45
|
+
generateSchemas: boolean;
|
|
46
|
+
generateMockData: boolean;
|
|
47
|
+
importData: boolean;
|
|
48
|
+
checkDuplicates: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type CollectionConfig = AppwriteConfig["collections"];
|
|
52
|
+
type ImportDef = CollectionConfig[number]["importDefs"][number];
|
|
53
|
+
type AttributeMappings = ImportDef["attributeMappings"];
|
|
54
|
+
type AfterImportAction = AttributeMappings[number]["postImportActions"][number];
|
|
55
|
+
type ValidityRule = AttributeMappings[number]["validationActions"][number];
|
|
56
|
+
|
|
57
|
+
export class UtilsController {
|
|
58
|
+
private appwriteFolderPath: string;
|
|
59
|
+
private appwriteConfigPath: string;
|
|
60
|
+
private config?: AppwriteConfig;
|
|
61
|
+
private appwriteServer?: Client;
|
|
62
|
+
private database?: Databases;
|
|
63
|
+
private storage?: Storage;
|
|
64
|
+
public converterDefinitions: ConverterFunctions = converterFunctions;
|
|
65
|
+
public validityRuleDefinitions: ValidationRules = validationRules;
|
|
66
|
+
public afterImportActionsDefinitions: AfterImportActions = afterImportActions;
|
|
67
|
+
|
|
68
|
+
constructor() {
|
|
69
|
+
const basePath = process.cwd(); // Gets the current working directory
|
|
70
|
+
const appwriteFolderPath = path.join(basePath, "src", "appwrite");
|
|
71
|
+
const appwriteConfigPath = path.join(
|
|
72
|
+
appwriteFolderPath,
|
|
73
|
+
"appwriteConfig.yaml"
|
|
74
|
+
);
|
|
75
|
+
this.appwriteFolderPath = appwriteFolderPath;
|
|
76
|
+
this.appwriteConfigPath = appwriteConfigPath;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// async loadCustomDefinitions(): Promise<void> {
|
|
80
|
+
// try {
|
|
81
|
+
// const customDefinitionsPath = path.join(
|
|
82
|
+
// this.appwriteFolderPath,
|
|
83
|
+
// "customDefinitions.ts"
|
|
84
|
+
// );
|
|
85
|
+
// if (fs.existsSync(customDefinitionsPath)) {
|
|
86
|
+
// // Dynamically import custom definitions
|
|
87
|
+
// const customDefinitions = (await import(
|
|
88
|
+
// customDefinitionsPath
|
|
89
|
+
// )) as typeof import("customDefinitions");
|
|
90
|
+
// this.converterDefinitions = {
|
|
91
|
+
// ...this.converterDefinitions,
|
|
92
|
+
// ...customDefinitions.converterDefinitions,
|
|
93
|
+
// };
|
|
94
|
+
// this.validityRuleDefinitions = {
|
|
95
|
+
// ...this.validityRuleDefinitions,
|
|
96
|
+
// ...customDefinitions.validityRuleDefinitions,
|
|
97
|
+
// };
|
|
98
|
+
// this.afterImportActionsDefinitions = {
|
|
99
|
+
// ...this.afterImportActionsDefinitions,
|
|
100
|
+
// ...customDefinitions.afterImportActionsDefinitions,
|
|
101
|
+
// };
|
|
102
|
+
// }
|
|
103
|
+
// } catch (error) {
|
|
104
|
+
// console.error("Failed to load custom definitions:", error);
|
|
105
|
+
// }
|
|
106
|
+
// }
|
|
107
|
+
|
|
108
|
+
async init() {
|
|
109
|
+
if (!this.config) {
|
|
110
|
+
console.log("Initializing appwrite client & loading config...");
|
|
111
|
+
this.config = await loadConfig(this.appwriteConfigPath);
|
|
112
|
+
this.appwriteServer = new Client()
|
|
113
|
+
.setEndpoint(this.config.appwriteEndpoint)
|
|
114
|
+
.setProject(this.config.appwriteProject)
|
|
115
|
+
.setKey(this.config.appwriteKey);
|
|
116
|
+
this.database = new Databases(this.appwriteServer);
|
|
117
|
+
this.storage = new Storage(this.appwriteServer);
|
|
118
|
+
this.config.appwriteClient = this.appwriteServer;
|
|
119
|
+
// await this.loadCustomDefinitions();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async run(options: SetupOptions) {
|
|
124
|
+
await this.init(); // Ensure initialization is done
|
|
125
|
+
if (!this.database || !this.storage || !this.config) {
|
|
126
|
+
throw new Error("Database or storage not initialized");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Start the setup
|
|
130
|
+
console.log(
|
|
131
|
+
"Starting setup, this step sets up migrations, runs backup, wipes databases, and updates schemas (depending on your options)..."
|
|
132
|
+
);
|
|
133
|
+
await startSetup(
|
|
134
|
+
this.database,
|
|
135
|
+
this.storage,
|
|
136
|
+
this.config,
|
|
137
|
+
options,
|
|
138
|
+
this.appwriteFolderPath
|
|
139
|
+
);
|
|
140
|
+
console.log("Setup complete.");
|
|
141
|
+
|
|
142
|
+
if (options.generateMockData) {
|
|
143
|
+
// TODO: Figure out how to do this dynamically
|
|
144
|
+
// await this.generateMockData();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (options.importData) {
|
|
148
|
+
console.log("Starting import data...");
|
|
149
|
+
const importDataActions = new ImportDataActions(
|
|
150
|
+
this.database,
|
|
151
|
+
this.storage,
|
|
152
|
+
this.config,
|
|
153
|
+
this.converterDefinitions,
|
|
154
|
+
this.validityRuleDefinitions,
|
|
155
|
+
this.afterImportActionsDefinitions
|
|
156
|
+
);
|
|
157
|
+
const importController = new ImportController(
|
|
158
|
+
this.config!,
|
|
159
|
+
this.database!,
|
|
160
|
+
this.storage!,
|
|
161
|
+
this.appwriteFolderPath,
|
|
162
|
+
importDataActions,
|
|
163
|
+
options
|
|
164
|
+
);
|
|
165
|
+
await importController.run();
|
|
166
|
+
console.log("Import data complete.");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// if (options.checkDuplicates) {
|
|
170
|
+
// await this.checkDuplicates();
|
|
171
|
+
// }
|
|
172
|
+
}
|
|
173
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Enable latest features
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"module": "NodeNext",
|
|
6
|
+
"lib": ["ESNext"],
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"allowArbitraryExtensions": true,
|
|
11
|
+
"allowSyntheticDefaultImports": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
|
|
14
|
+
// NodeNext mode
|
|
15
|
+
"moduleResolution": "NodeNext",
|
|
16
|
+
"verbatimModuleSyntax": true,
|
|
17
|
+
"noEmit": false,
|
|
18
|
+
"emitDeclarationOnly": false,
|
|
19
|
+
|
|
20
|
+
// Best practices
|
|
21
|
+
"strict": true,
|
|
22
|
+
"skipLibCheck": true,
|
|
23
|
+
"noFallthroughCasesInSwitch": true,
|
|
24
|
+
|
|
25
|
+
// Some stricter flags (disabled by default)
|
|
26
|
+
"noUnusedLocals": false,
|
|
27
|
+
"noUnusedParameters": false,
|
|
28
|
+
"noPropertyAccessFromIndexSignature": false,
|
|
29
|
+
"baseUrl": ".",
|
|
30
|
+
"esModuleInterop": true,
|
|
31
|
+
"outDir": "./dist",
|
|
32
|
+
"rootDir": "./src",
|
|
33
|
+
"declaration": true
|
|
34
|
+
},
|
|
35
|
+
"include": ["src/main.ts", "src/**/*"],
|
|
36
|
+
"exclude": ["node_modules", "dist", "src/appwrite", "example/*"]
|
|
37
|
+
}
|