appwrite-cli 6.0.0-rc.1 → 6.0.0-rc.3
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 +2 -2
- package/index.js +2 -1
- package/install.ps1 +2 -2
- package/install.sh +1 -1
- package/lib/client.js +4 -4
- package/lib/commands/generic.js +17 -5
- package/lib/commands/init.js +26 -15
- package/lib/commands/pull.js +147 -68
- package/lib/commands/push.js +74 -150
- package/lib/commands/run.js +53 -45
- package/lib/config.js +73 -20
- package/lib/emulation/docker.js +70 -62
- package/lib/emulation/utils.js +15 -6
- package/lib/parser.js +16 -6
- package/lib/questions.js +56 -21
- package/lib/sdks.js +1 -39
- package/package.json +1 -1
- package/scoop/appwrite.json +3 -3
package/lib/commands/run.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
const Tail = require('tail').Tail;
|
|
2
|
-
const
|
|
2
|
+
const chalk = require('chalk');
|
|
3
3
|
const ignore = require("ignore");
|
|
4
4
|
const tar = require("tar");
|
|
5
5
|
const fs = require("fs");
|
|
6
|
-
const ID = require("../id");
|
|
7
|
-
const childProcess = require('child_process');
|
|
8
6
|
const chokidar = require('chokidar');
|
|
9
7
|
const inquirer = require("inquirer");
|
|
10
8
|
const path = require("path");
|
|
@@ -12,13 +10,11 @@ const { Command } = require("commander");
|
|
|
12
10
|
const { localConfig, globalConfig } = require("../config");
|
|
13
11
|
const { paginate } = require('../paginate');
|
|
14
12
|
const { functionsListVariables } = require('./functions');
|
|
15
|
-
const { usersGet, usersCreateJWT } = require('./users');
|
|
16
|
-
const { projectsCreateJWT } = require('./projects');
|
|
17
13
|
const { questionsRunFunctions } = require("../questions");
|
|
18
|
-
const { actionRunner, success, log, error, commandDescriptions, drawTable } = require("../parser");
|
|
14
|
+
const { actionRunner, success, log, warn, error, hint, commandDescriptions, drawTable } = require("../parser");
|
|
19
15
|
const { systemHasCommand, isPortTaken, getAllFiles } = require('../utils');
|
|
20
|
-
const {
|
|
21
|
-
const { dockerStop, dockerCleanup, dockerStart, dockerBuild, dockerPull
|
|
16
|
+
const { runtimeNames, systemTools, JwtManager, Queue } = require('../emulation/utils');
|
|
17
|
+
const { dockerStop, dockerCleanup, dockerStart, dockerBuild, dockerPull } = require('../emulation/docker');
|
|
22
18
|
|
|
23
19
|
const runFunction = async ({ port, functionId, noVariables, noReload, userId } = {}) => {
|
|
24
20
|
// Selection
|
|
@@ -68,7 +64,7 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
|
|
|
68
64
|
}
|
|
69
65
|
|
|
70
66
|
if(!portFound) {
|
|
71
|
-
error(
|
|
67
|
+
error("Could not find an available port. Please select a port with 'appwrite run --port YOUR_PORT' command.");
|
|
72
68
|
return;
|
|
73
69
|
}
|
|
74
70
|
}
|
|
@@ -86,16 +82,16 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
|
|
|
86
82
|
commands: func.commands,
|
|
87
83
|
};
|
|
88
84
|
|
|
89
|
-
log("Local function configuration:");
|
|
90
85
|
drawTable([settings]);
|
|
91
|
-
log(
|
|
86
|
+
log("If you wish to change your local settings, update the appwrite.json file and rerun the 'appwrite run' command.");
|
|
87
|
+
hint("Permissions, events, CRON and timeouts dont apply when running locally.");
|
|
92
88
|
|
|
93
|
-
await dockerCleanup();
|
|
89
|
+
await dockerCleanup(func.$id);
|
|
94
90
|
|
|
95
91
|
process.on('SIGINT', async () => {
|
|
96
92
|
log('Cleaning up ...');
|
|
97
|
-
await dockerCleanup();
|
|
98
|
-
success();
|
|
93
|
+
await dockerCleanup(func.$id);
|
|
94
|
+
success("Local function successfully stopped.");
|
|
99
95
|
process.exit();
|
|
100
96
|
});
|
|
101
97
|
|
|
@@ -116,22 +112,17 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
|
|
|
116
112
|
|
|
117
113
|
const variables = {};
|
|
118
114
|
if(!noVariables) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
});
|
|
131
|
-
} catch(err) {
|
|
132
|
-
log("Could not fetch remote variables: " + err.message);
|
|
133
|
-
log("Function will run locally, but will not have your function's environment variables set.");
|
|
134
|
-
}
|
|
115
|
+
try {
|
|
116
|
+
const { variables: remoteVariables } = await paginate(functionsListVariables, {
|
|
117
|
+
functionId: func['$id'],
|
|
118
|
+
parseOutput: false
|
|
119
|
+
}, 100, 'variables');
|
|
120
|
+
|
|
121
|
+
remoteVariables.forEach((v) => {
|
|
122
|
+
variables[v.key] = v.value;
|
|
123
|
+
});
|
|
124
|
+
} catch(err) {
|
|
125
|
+
warn("Remote variables not fetched. Production environment variables will not be avaiable. Reason: " + err.message);
|
|
135
126
|
}
|
|
136
127
|
}
|
|
137
128
|
|
|
@@ -143,7 +134,11 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
|
|
|
143
134
|
variables['APPWRITE_FUNCTION_RUNTIME_NAME'] = runtimeNames[runtimeName] ?? '';
|
|
144
135
|
variables['APPWRITE_FUNCTION_RUNTIME_VERSION'] = func.runtime;
|
|
145
136
|
|
|
146
|
-
|
|
137
|
+
try {
|
|
138
|
+
await JwtManager.setup(userId, func.scopes ?? []);
|
|
139
|
+
} catch(err) {
|
|
140
|
+
warn("Dynamic API key not generated. Header x-appwrite-key will not be set. Reason: " + err.message);
|
|
141
|
+
}
|
|
147
142
|
|
|
148
143
|
const headers = {};
|
|
149
144
|
headers['x-appwrite-key'] = JwtManager.functionJwt ?? '';
|
|
@@ -154,14 +149,12 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
|
|
|
154
149
|
variables['OPEN_RUNTIMES_HEADERS'] = JSON.stringify(headers);
|
|
155
150
|
|
|
156
151
|
await dockerPull(func);
|
|
157
|
-
await dockerBuild(func, variables);
|
|
158
|
-
await dockerStart(func, variables, port);
|
|
159
152
|
|
|
160
153
|
new Tail(logsPath).on("line", function(data) {
|
|
161
|
-
|
|
154
|
+
process.stdout.write(chalk.white(`${data}\n`));
|
|
162
155
|
});
|
|
163
156
|
new Tail(errorsPath).on("line", function(data) {
|
|
164
|
-
|
|
157
|
+
process.stdout.write(chalk.white(`${data}\n`));
|
|
165
158
|
});
|
|
166
159
|
|
|
167
160
|
if(!noReload) {
|
|
@@ -177,23 +170,22 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
|
|
|
177
170
|
Queue.events.on('reload', async ({ files }) => {
|
|
178
171
|
Queue.lock();
|
|
179
172
|
|
|
180
|
-
log('Live-reloading due to file changes: ');
|
|
181
|
-
for(const file of files) {
|
|
182
|
-
log(`- ${file}`);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
173
|
try {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
await dockerStopActive();
|
|
174
|
+
await dockerStop(func.$id);
|
|
189
175
|
|
|
190
176
|
const dependencyFile = files.find((filePath) => tool.dependencyFiles.includes(filePath));
|
|
191
177
|
if(tool.isCompiled || dependencyFile) {
|
|
192
|
-
log(`Rebuilding the function due to
|
|
178
|
+
log(`Rebuilding the function due to file changes ...`);
|
|
193
179
|
await dockerBuild(func, variables);
|
|
180
|
+
|
|
181
|
+
if(!Queue.isEmpty()) {
|
|
182
|
+
Queue.unlock();
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
194
186
|
await dockerStart(func, variables, port);
|
|
195
187
|
} else {
|
|
196
|
-
log('Hot-swapping function files
|
|
188
|
+
log('Hot-swapping function.. Files with change are ' + files.join(', '));
|
|
197
189
|
|
|
198
190
|
const functionPath = path.join(process.cwd(), func.path);
|
|
199
191
|
const hotSwapPath = path.join(functionPath, '.appwrite/hot-swap');
|
|
@@ -255,6 +247,22 @@ const runFunction = async ({ port, functionId, noVariables, noReload, userId } =
|
|
|
255
247
|
Queue.unlock();
|
|
256
248
|
}
|
|
257
249
|
});
|
|
250
|
+
|
|
251
|
+
Queue.lock();
|
|
252
|
+
|
|
253
|
+
log('Building function using Docker ...');
|
|
254
|
+
await dockerBuild(func, variables);
|
|
255
|
+
|
|
256
|
+
if(!Queue.isEmpty()) {
|
|
257
|
+
Queue.unlock();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
log('Starting function using Docker ...');
|
|
262
|
+
hint('Function automatically restarts when you edit your code.');
|
|
263
|
+
await dockerStart(func, variables, port);
|
|
264
|
+
|
|
265
|
+
Queue.unlock();
|
|
258
266
|
}
|
|
259
267
|
|
|
260
268
|
const run = new Command("run")
|
package/lib/config.js
CHANGED
|
@@ -4,6 +4,59 @@ const _path = require("path");
|
|
|
4
4
|
const process = require("process");
|
|
5
5
|
const JSONbig = require("json-bigint")({ storeAsString: false });
|
|
6
6
|
|
|
7
|
+
const KeysFunction = new Set(["path", "$id", "execute", "name", "enabled", "logging", "runtime", "scopes", "events", "schedule", "timeout", "entrypoint", "commands"]);
|
|
8
|
+
const KeysDatabase = new Set(["$id", "name", "enabled"]);
|
|
9
|
+
const KeysCollection = new Set(["$id", "$permissions", "databaseId", "name", "enabled", "documentSecurity", "attributes", "indexes"]);
|
|
10
|
+
const KeysStorage = new Set(["$id", "$permissions", "fileSecurity", "name", "enabled", "maximumFileSize", "allowedFileExtensions", "compression", "encryption", "antivirus"]);
|
|
11
|
+
const KeyTopics = new Set(["$id", "name", "subscribe"]);
|
|
12
|
+
const KeyAttributes = new Set([
|
|
13
|
+
"key",
|
|
14
|
+
"type",
|
|
15
|
+
"required",
|
|
16
|
+
"array",
|
|
17
|
+
"size",
|
|
18
|
+
"default",
|
|
19
|
+
// integer and float
|
|
20
|
+
"min",
|
|
21
|
+
"max",
|
|
22
|
+
// email, enum, URL, IP, and datetime
|
|
23
|
+
"format",
|
|
24
|
+
// enum
|
|
25
|
+
"elements",
|
|
26
|
+
// relationship
|
|
27
|
+
"relatedCollection",
|
|
28
|
+
"relationType",
|
|
29
|
+
"twoWay",
|
|
30
|
+
"twoWayKey",
|
|
31
|
+
"onDelete",
|
|
32
|
+
"side"
|
|
33
|
+
]);
|
|
34
|
+
const KeyIndexes = new Set(["key", "type", "status", "attributes", "orders"]);
|
|
35
|
+
|
|
36
|
+
function whitelistKeys(value, keys, nestedKeys = {}) {
|
|
37
|
+
if(Array.isArray(value)) {
|
|
38
|
+
const newValue = [];
|
|
39
|
+
|
|
40
|
+
for(const item of value) {
|
|
41
|
+
newValue.push(whitelistKeys(item, keys, nestedKeys));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return newValue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const newValue = {};
|
|
48
|
+
Object.keys(value).forEach((key) => {
|
|
49
|
+
if(keys.has(key)) {
|
|
50
|
+
if(nestedKeys[key]) {
|
|
51
|
+
newValue[key] = whitelistKeys(value[key], nestedKeys[key]);
|
|
52
|
+
} else {
|
|
53
|
+
newValue[key] = value[key];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return newValue;
|
|
58
|
+
}
|
|
59
|
+
|
|
7
60
|
class Config {
|
|
8
61
|
constructor(path) {
|
|
9
62
|
this.path = path;
|
|
@@ -94,6 +147,8 @@ class Local extends Config {
|
|
|
94
147
|
}
|
|
95
148
|
|
|
96
149
|
addFunction(props) {
|
|
150
|
+
props = whitelistKeys(props, KeysFunction);
|
|
151
|
+
|
|
97
152
|
if (!this.has("functions")) {
|
|
98
153
|
this.set("functions", []);
|
|
99
154
|
}
|
|
@@ -101,21 +156,6 @@ class Local extends Config {
|
|
|
101
156
|
let functions = this.get("functions");
|
|
102
157
|
for (let i = 0; i < functions.length; i++) {
|
|
103
158
|
if (functions[i]['$id'] == props['$id']) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
functions.push(props);
|
|
108
|
-
this.set("functions", functions);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
updateFunction(id, props) {
|
|
112
|
-
if (!this.has("functions")) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
let functions = this.get("functions");
|
|
117
|
-
for (let i = 0; i < functions.length; i++) {
|
|
118
|
-
if (functions[i]['$id'] == id) {
|
|
119
159
|
functions[i] = {
|
|
120
160
|
...functions[i],
|
|
121
161
|
...props
|
|
@@ -124,6 +164,9 @@ class Local extends Config {
|
|
|
124
164
|
return;
|
|
125
165
|
}
|
|
126
166
|
}
|
|
167
|
+
|
|
168
|
+
functions.push(props);
|
|
169
|
+
this.set("functions", functions);
|
|
127
170
|
}
|
|
128
171
|
|
|
129
172
|
getCollections() {
|
|
@@ -149,6 +192,11 @@ class Local extends Config {
|
|
|
149
192
|
}
|
|
150
193
|
|
|
151
194
|
addCollection(props) {
|
|
195
|
+
props = whitelistKeys(props, KeysCollection, {
|
|
196
|
+
attributes: KeyAttributes,
|
|
197
|
+
indexes: KeyIndexes
|
|
198
|
+
});
|
|
199
|
+
|
|
152
200
|
if (!this.has("collections")) {
|
|
153
201
|
this.set("collections", []);
|
|
154
202
|
}
|
|
@@ -188,6 +236,8 @@ class Local extends Config {
|
|
|
188
236
|
}
|
|
189
237
|
|
|
190
238
|
addBucket(props) {
|
|
239
|
+
props = whitelistKeys(props, KeysStorage);
|
|
240
|
+
|
|
191
241
|
if (!this.has("buckets")) {
|
|
192
242
|
this.set("buckets", []);
|
|
193
243
|
}
|
|
@@ -227,6 +277,8 @@ class Local extends Config {
|
|
|
227
277
|
}
|
|
228
278
|
|
|
229
279
|
addMessagingTopic(props) {
|
|
280
|
+
props = whitelistKeys(props, KeyTopics);
|
|
281
|
+
|
|
230
282
|
if (!this.has("topics")) {
|
|
231
283
|
this.set("topics", []);
|
|
232
284
|
}
|
|
@@ -266,6 +318,8 @@ class Local extends Config {
|
|
|
266
318
|
}
|
|
267
319
|
|
|
268
320
|
addDatabase(props) {
|
|
321
|
+
props = whitelistKeys(props, KeysDatabase);
|
|
322
|
+
|
|
269
323
|
if (!this.has("databases")) {
|
|
270
324
|
this.set("databases", []);
|
|
271
325
|
}
|
|
@@ -329,7 +383,7 @@ class Local extends Config {
|
|
|
329
383
|
return {
|
|
330
384
|
projectId: this.get("projectId"),
|
|
331
385
|
projectName: this.get("projectName"),
|
|
332
|
-
projectSettings: this.get('
|
|
386
|
+
projectSettings: this.get('settings')
|
|
333
387
|
};
|
|
334
388
|
}
|
|
335
389
|
|
|
@@ -357,7 +411,6 @@ class Local extends Config {
|
|
|
357
411
|
functions: projectSettings.serviceStatusForFunctions,
|
|
358
412
|
graphql: projectSettings.serviceStatusForGraphql,
|
|
359
413
|
messaging: projectSettings.serviceStatusForMessaging,
|
|
360
|
-
|
|
361
414
|
},
|
|
362
415
|
auth: {
|
|
363
416
|
methods: {
|
|
@@ -380,7 +433,7 @@ class Local extends Config {
|
|
|
380
433
|
}
|
|
381
434
|
};
|
|
382
435
|
|
|
383
|
-
this.set('
|
|
436
|
+
this.set('settings', settings)
|
|
384
437
|
}
|
|
385
438
|
|
|
386
439
|
}
|
|
@@ -520,7 +573,7 @@ class Global extends Config {
|
|
|
520
573
|
const current = this.getCurrentSession();
|
|
521
574
|
|
|
522
575
|
if (current) {
|
|
523
|
-
const config = this.get(current);
|
|
576
|
+
const config = this.get(current) ?? {};
|
|
524
577
|
|
|
525
578
|
return config[key] !== undefined;
|
|
526
579
|
}
|
|
@@ -530,7 +583,7 @@ class Global extends Config {
|
|
|
530
583
|
const current = this.getCurrentSession();
|
|
531
584
|
|
|
532
585
|
if (current) {
|
|
533
|
-
const config = this.get(current);
|
|
586
|
+
const config = this.get(current) ?? {};
|
|
534
587
|
|
|
535
588
|
return config[key];
|
|
536
589
|
}
|
package/lib/emulation/docker.js
CHANGED
|
@@ -1,45 +1,43 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
1
2
|
const childProcess = require('child_process');
|
|
2
3
|
const { localConfig } = require("../config");
|
|
3
4
|
const path = require('path');
|
|
4
5
|
const fs = require('fs');
|
|
5
|
-
const { log,success } = require("../parser");
|
|
6
|
-
const { openRuntimesVersion, systemTools } = require("./utils");
|
|
7
|
-
const ID = require("../id");
|
|
8
|
-
|
|
9
|
-
const activeDockerIds = {};
|
|
6
|
+
const { log, success, hint } = require("../parser");
|
|
7
|
+
const { openRuntimesVersion, systemTools, Queue } = require("./utils");
|
|
10
8
|
|
|
11
9
|
async function dockerStop(id) {
|
|
12
|
-
delete activeDockerIds[id];
|
|
13
10
|
const stopProcess = childProcess.spawn('docker', ['rm', '--force', id], {
|
|
14
11
|
stdio: 'pipe',
|
|
12
|
+
env: {
|
|
13
|
+
...process.env,
|
|
14
|
+
DOCKER_CLI_HINTS: 'false'
|
|
15
|
+
}
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
await new Promise((res) => { stopProcess.on('close', res) });
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
async function dockerPull(func) {
|
|
21
|
-
log('Pulling Docker image of function runtime ...');
|
|
22
|
-
|
|
23
22
|
const runtimeChunks = func.runtime.split("-");
|
|
24
23
|
const runtimeVersion = runtimeChunks.pop();
|
|
25
24
|
const runtimeName = runtimeChunks.join("-");
|
|
26
25
|
const imageName = `openruntimes/${runtimeName}:${openRuntimesVersion}-${runtimeVersion}`;
|
|
27
26
|
|
|
27
|
+
log('Verifying Docker image ...');
|
|
28
|
+
|
|
28
29
|
const pullProcess = childProcess.spawn('docker', ['pull', imageName], {
|
|
29
30
|
stdio: 'pipe',
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
process.stderr.write(`\n${data}$ `);
|
|
31
|
+
env: {
|
|
32
|
+
...process.env,
|
|
33
|
+
DOCKER_CLI_HINTS: 'false'
|
|
34
|
+
}
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
await new Promise((res) => { pullProcess.on('close', res) });
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
async function dockerBuild(func, variables) {
|
|
41
|
-
log('Building function using Docker ...');
|
|
42
|
-
|
|
43
41
|
const runtimeChunks = func.runtime.split("-");
|
|
44
42
|
const runtimeVersion = runtimeChunks.pop();
|
|
45
43
|
const runtimeName = runtimeChunks.join("-");
|
|
@@ -47,7 +45,7 @@ async function dockerBuild(func, variables) {
|
|
|
47
45
|
|
|
48
46
|
const functionDir = path.join(process.cwd(), func.path);
|
|
49
47
|
|
|
50
|
-
const id =
|
|
48
|
+
const id = func.$id;
|
|
51
49
|
|
|
52
50
|
const params = [ 'run' ];
|
|
53
51
|
params.push('--name', id);
|
|
@@ -65,19 +63,40 @@ async function dockerBuild(func, variables) {
|
|
|
65
63
|
|
|
66
64
|
const buildProcess = childProcess.spawn('docker', params, {
|
|
67
65
|
stdio: 'pipe',
|
|
68
|
-
pwd: functionDir
|
|
66
|
+
pwd: functionDir,
|
|
67
|
+
env: {
|
|
68
|
+
...process.env,
|
|
69
|
+
DOCKER_CLI_HINTS: 'false'
|
|
70
|
+
}
|
|
69
71
|
});
|
|
70
72
|
|
|
71
73
|
buildProcess.stdout.on('data', (data) => {
|
|
72
|
-
process.stdout.write(
|
|
74
|
+
process.stdout.write(chalk.blackBright(`${data}\n`));
|
|
73
75
|
});
|
|
74
76
|
|
|
75
77
|
buildProcess.stderr.on('data', (data) => {
|
|
76
|
-
process.stderr.write(
|
|
78
|
+
process.stderr.write(chalk.blackBright(`${data}\n`));
|
|
77
79
|
});
|
|
78
80
|
|
|
81
|
+
const killInterval = setInterval(() => {
|
|
82
|
+
if(!Queue.isEmpty()) {
|
|
83
|
+
log('Cancelling build ...');
|
|
84
|
+
buildProcess.stdout.destroy();
|
|
85
|
+
buildProcess.stdin.destroy();
|
|
86
|
+
buildProcess.stderr.destroy();
|
|
87
|
+
buildProcess.kill("SIGKILL");
|
|
88
|
+
clearInterval(killInterval);
|
|
89
|
+
}
|
|
90
|
+
}, 100);
|
|
91
|
+
|
|
79
92
|
await new Promise((res) => { buildProcess.on('close', res) });
|
|
80
93
|
|
|
94
|
+
clearInterval(interval);
|
|
95
|
+
|
|
96
|
+
if(!Queue.isEmpty()) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
81
100
|
const copyPath = path.join(process.cwd(), func.path, '.appwrite', 'build.tar.gz');
|
|
82
101
|
const copyDir = path.dirname(copyPath);
|
|
83
102
|
if (!fs.existsSync(copyDir)) {
|
|
@@ -86,19 +105,16 @@ async function dockerBuild(func, variables) {
|
|
|
86
105
|
|
|
87
106
|
const copyProcess = childProcess.spawn('docker', ['cp', `${id}:/mnt/code/code.tar.gz`, copyPath], {
|
|
88
107
|
stdio: 'pipe',
|
|
89
|
-
pwd: functionDir
|
|
108
|
+
pwd: functionDir,
|
|
109
|
+
env: {
|
|
110
|
+
...process.env,
|
|
111
|
+
DOCKER_CLI_HINTS: 'false'
|
|
112
|
+
}
|
|
90
113
|
});
|
|
91
114
|
|
|
92
115
|
await new Promise((res) => { copyProcess.on('close', res) });
|
|
93
116
|
|
|
94
|
-
|
|
95
|
-
stdio: 'pipe',
|
|
96
|
-
pwd: functionDir
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
await new Promise((res) => { cleanupProcess.on('close', res) });
|
|
100
|
-
|
|
101
|
-
delete activeDockerIds[id];
|
|
117
|
+
await dockerStop(id);
|
|
102
118
|
|
|
103
119
|
const tempPath = path.join(process.cwd(), func.path, 'code.tar.gz');
|
|
104
120
|
if (fs.existsSync(tempPath)) {
|
|
@@ -107,15 +123,6 @@ async function dockerBuild(func, variables) {
|
|
|
107
123
|
}
|
|
108
124
|
|
|
109
125
|
async function dockerStart(func, variables, port) {
|
|
110
|
-
log('Starting function using Docker ...');
|
|
111
|
-
|
|
112
|
-
log("Permissions, events, CRON and timeouts dont apply when running locally.");
|
|
113
|
-
|
|
114
|
-
log('💡 Hint: Function automatically restarts when you edit your code.');
|
|
115
|
-
|
|
116
|
-
success(`Visit http://localhost:${port}/ to execute your function.`);
|
|
117
|
-
|
|
118
|
-
|
|
119
126
|
const runtimeChunks = func.runtime.split("-");
|
|
120
127
|
const runtimeVersion = runtimeChunks.pop();
|
|
121
128
|
const runtimeName = runtimeChunks.join("-");
|
|
@@ -125,11 +132,10 @@ async function dockerStart(func, variables, port) {
|
|
|
125
132
|
|
|
126
133
|
const functionDir = path.join(process.cwd(), func.path);
|
|
127
134
|
|
|
128
|
-
const id =
|
|
135
|
+
const id = func.$id;
|
|
129
136
|
|
|
130
137
|
const params = [ 'run' ];
|
|
131
138
|
params.push('--rm');
|
|
132
|
-
params.push('-d');
|
|
133
139
|
params.push('--name', id);
|
|
134
140
|
params.push('-p', `${port}:3000`);
|
|
135
141
|
params.push('-e', 'APPWRITE_ENV=development');
|
|
@@ -145,43 +151,45 @@ async function dockerStart(func, variables, port) {
|
|
|
145
151
|
params.push('-v', `${functionDir}/.appwrite/build.tar.gz:/mnt/code/code.tar.gz:ro`);
|
|
146
152
|
params.push(imageName, 'sh', '-c', `helpers/start.sh "${tool.startCommand}"`);
|
|
147
153
|
|
|
148
|
-
childProcess.spawn('docker', params, {
|
|
154
|
+
const startProcess = childProcess.spawn('docker', params, {
|
|
149
155
|
stdio: 'pipe',
|
|
150
|
-
pwd: functionDir
|
|
156
|
+
pwd: functionDir,
|
|
157
|
+
env: {
|
|
158
|
+
...process.env,
|
|
159
|
+
DOCKER_CLI_HINTS: 'false'
|
|
160
|
+
}
|
|
151
161
|
});
|
|
152
162
|
|
|
153
|
-
|
|
154
|
-
|
|
163
|
+
startProcess.stdout.on('data', (data) => {
|
|
164
|
+
process.stdout.write(chalk.blackBright(data));
|
|
165
|
+
});
|
|
155
166
|
|
|
156
|
-
|
|
157
|
-
|
|
167
|
+
startProcess.stderr.on('data', (data) => {
|
|
168
|
+
process.stdout.write(chalk.blackBright(data));
|
|
169
|
+
});
|
|
158
170
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const appwritePath = path.join(process.cwd(), func.path, '.appwrite');
|
|
162
|
-
if (fs.existsSync(appwritePath)) {
|
|
163
|
-
fs.rmSync(appwritePath, { recursive: true, force: true });
|
|
164
|
-
}
|
|
171
|
+
success(`Visit http://localhost:${port}/ to execute your function.`);
|
|
172
|
+
}
|
|
165
173
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
174
|
+
async function dockerCleanup(functionId) {
|
|
175
|
+
await dockerStop(functionId);
|
|
176
|
+
|
|
177
|
+
const func = localConfig.getFunction(functionId);
|
|
178
|
+
const appwritePath = path.join(process.cwd(), func.path, '.appwrite');
|
|
179
|
+
if (fs.existsSync(appwritePath)) {
|
|
180
|
+
fs.rmSync(appwritePath, { recursive: true, force: true });
|
|
170
181
|
}
|
|
171
|
-
}
|
|
172
182
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
await dockerStop(id);
|
|
183
|
+
const tempPath = path.join(process.cwd(), func.path, 'code.tar.gz');
|
|
184
|
+
if (fs.existsSync(tempPath)) {
|
|
185
|
+
fs.rmSync(tempPath, { force: true });
|
|
177
186
|
}
|
|
178
187
|
}
|
|
179
188
|
|
|
180
189
|
module.exports = {
|
|
181
|
-
dockerStop,
|
|
182
190
|
dockerPull,
|
|
183
191
|
dockerBuild,
|
|
184
192
|
dockerStart,
|
|
185
193
|
dockerCleanup,
|
|
186
|
-
|
|
194
|
+
dockerStop,
|
|
187
195
|
}
|
package/lib/emulation/utils.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
const EventEmitter = require('node:events');
|
|
2
2
|
const { projectsCreateJWT } = require('../commands/projects');
|
|
3
3
|
const { localConfig } = require("../config");
|
|
4
|
+
const { usersGet, usersCreateJWT } = require("../commands/users");
|
|
5
|
+
const { log } = require("../parser");
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
const openRuntimesVersion = 'v3';
|
|
7
|
+
const openRuntimesVersion = 'v4';
|
|
7
8
|
|
|
8
9
|
const runtimeNames = {
|
|
9
10
|
'node': 'Node.js',
|
|
@@ -17,7 +18,8 @@ const runtimeNames = {
|
|
|
17
18
|
'java': 'Java',
|
|
18
19
|
'swift': 'Swift',
|
|
19
20
|
'kotlin': 'Kotlin',
|
|
20
|
-
'bun': 'Bun'
|
|
21
|
+
'bun': 'Bun',
|
|
22
|
+
'go': 'Go',
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
const systemTools = {
|
|
@@ -81,6 +83,11 @@ const systemTools = {
|
|
|
81
83
|
startCommand: "bun src/server.ts",
|
|
82
84
|
dependencyFiles: [ "package.json", "package-lock.json", "bun.lockb" ]
|
|
83
85
|
},
|
|
86
|
+
'go': {
|
|
87
|
+
isCompiled: true,
|
|
88
|
+
startCommand: "src/function/server",
|
|
89
|
+
dependencyFiles: [ ]
|
|
90
|
+
},
|
|
84
91
|
};
|
|
85
92
|
|
|
86
93
|
const JwtManager = {
|
|
@@ -90,7 +97,7 @@ const JwtManager = {
|
|
|
90
97
|
timerWarn: null,
|
|
91
98
|
timerError: null,
|
|
92
99
|
|
|
93
|
-
async setup(userId = null) {
|
|
100
|
+
async setup(userId = null, projectScopes = []) {
|
|
94
101
|
if(this.timerWarn) {
|
|
95
102
|
clearTimeout(this.timerWarn);
|
|
96
103
|
}
|
|
@@ -123,8 +130,7 @@ const JwtManager = {
|
|
|
123
130
|
|
|
124
131
|
const functionResponse = await projectsCreateJWT({
|
|
125
132
|
projectId: localConfig.getProject().projectId,
|
|
126
|
-
|
|
127
|
-
scopes: ["sessions.write","users.read","users.write","teams.read","teams.write","databases.read","databases.write","collections.read","collections.write","attributes.read","attributes.write","indexes.read","indexes.write","documents.read","documents.write","files.read","files.write","buckets.read","buckets.write","functions.read","functions.write","execution.read","execution.write","locale.read","avatars.read","health.read","providers.read","providers.write","messages.read","messages.write","topics.read","topics.write","subscribers.read","subscribers.write","targets.read","targets.write","rules.read","rules.write","migrations.read","migrations.write","vcs.read","vcs.write","assistant.read"],
|
|
133
|
+
scopes: projectScopes,
|
|
128
134
|
duration: 60*60,
|
|
129
135
|
parseOutput: false
|
|
130
136
|
});
|
|
@@ -150,6 +156,9 @@ const Queue = {
|
|
|
150
156
|
this.files = [];
|
|
151
157
|
this.locked = true;
|
|
152
158
|
},
|
|
159
|
+
isEmpty() {
|
|
160
|
+
return this.files.length === 0
|
|
161
|
+
},
|
|
153
162
|
unlock() {
|
|
154
163
|
this.locked = false;
|
|
155
164
|
if(this.files.length > 0) {
|