diff --git a/README.md b/README.md index 92c6953..4a1b614 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Appwrite Command Line SDK ![License](https://img.shields.io/github/license/appwrite/sdk-for-cli.svg?style=flat-square) -![Version](https://img.shields.io/badge/api%20version-1.4.0-blue.svg?style=flat-square) +![Version](https://img.shields.io/badge/api%20version-1.4.2-blue.svg?style=flat-square) [![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator) [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord) @@ -29,7 +29,7 @@ Once the installation is complete, you can verify the install using ```sh $ appwrite -v -3.0.0 +4.0.0 ``` ### Install using prebuilt binaries @@ -63,7 +63,7 @@ $ scoop install https://raw.githubusercontent.com/appwrite/sdk-for-cli/master/sc Once the installation completes, you can verify your install using ``` $ appwrite -v -3.0.0 +4.0.0 ``` ## Getting Started diff --git a/docs/examples/functions/create.md b/docs/examples/functions/create.md index fa5c159..4fdcb17 100644 --- a/docs/examples/functions/create.md +++ b/docs/examples/functions/create.md @@ -1,7 +1,7 @@ appwrite functions create \ --functionId [FUNCTION_ID] \ --name [NAME] \ - --runtime node-14.5 \ + --runtime node-18.0 \ diff --git a/docs/examples/functions/update.md b/docs/examples/functions/update.md index d63f643..fb46789 100644 --- a/docs/examples/functions/update.md +++ b/docs/examples/functions/update.md @@ -1,7 +1,7 @@ appwrite functions update \ --functionId [FUNCTION_ID] \ --name [NAME] \ - --runtime node-14.5 \ + diff --git a/docs/examples/teams/create-membership.md b/docs/examples/teams/create-membership.md index 6faa89d..7842d11 100644 --- a/docs/examples/teams/create-membership.md +++ b/docs/examples/teams/create-membership.md @@ -1,7 +1,7 @@ appwrite teams createMembership \ --teamId [TEAM_ID] \ --roles one two three \ - --url https://example.com \ + diff --git a/install.ps1 b/install.ps1 index 3600998..e39dc89 100644 --- a/install.ps1 +++ b/install.ps1 @@ -13,8 +13,8 @@ # You can use "View source" of this page to see the full script. # REPO -$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/3.0.0/appwrite-cli-win-x64.exe" -$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/3.0.0/appwrite-cli-win-arm64.exe" +$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/4.0.0/appwrite-cli-win-x64.exe" +$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/4.0.0/appwrite-cli-win-arm64.exe" $APPWRITE_BINARY_NAME = "appwrite.exe" diff --git a/install.sh b/install.sh index 9b8d130..2110683 100644 --- a/install.sh +++ b/install.sh @@ -97,7 +97,7 @@ printSuccess() { downloadBinary() { echo "[2/4] Downloading executable for $OS ($ARCH) ..." - GITHUB_LATEST_VERSION="3.0.0" + GITHUB_LATEST_VERSION="4.0.0" GITHUB_FILE="appwrite-cli-${OS}-${ARCH}" GITHUB_URL="https://github.com/$GITHUB_REPOSITORY_NAME/releases/download/$GITHUB_LATEST_VERSION/$GITHUB_FILE" diff --git a/lib/client.js b/lib/client.js index 35470ed..77795ab 100644 --- a/lib/client.js +++ b/lib/client.js @@ -16,8 +16,8 @@ class Client { 'x-sdk-name': 'Command Line', 'x-sdk-platform': 'console', 'x-sdk-language': 'cli', - 'x-sdk-version': '3.0.0', - 'user-agent' : `AppwriteCLI/3.0.0 (${os.type()} ${os.version()}; ${os.arch()})`, + 'x-sdk-version': '4.0.0', + 'user-agent' : `AppwriteCLI/4.0.0 (${os.type()} ${os.version()}; ${os.arch()})`, 'X-Appwrite-Response-Format' : '1.4.0', }; } diff --git a/lib/commands/deploy.js b/lib/commands/deploy.js index b2a41dd..7b06dd5 100644 --- a/lib/commands/deploy.js +++ b/lib/commands/deploy.js @@ -206,10 +206,14 @@ const deployFunction = async ({ functionId, all, yes } = {}) => { functionId: func['$id'], name: func.name, execute: func.execute, - vars: JSON.stringify(response.vars), events: func.events, schedule: func.schedule, timeout: func.timeout, + enabled: func.enabled, + logging: func.logging, + entrypoint: func.entrypoint, + commands: func.commands, + vars: JSON.stringify(response.vars), parseOutput: false }); } catch (e) { @@ -220,10 +224,14 @@ const deployFunction = async ({ functionId, all, yes } = {}) => { name: func.name, runtime: func.runtime, execute: func.execute, - vars: JSON.stringify(func.vars), events: func.events, schedule: func.schedule, timeout: func.timeout, + enabled: func.enabled, + logging: func.logging, + entrypoint: func.entrypoint, + commands: func.commands, + vars: JSON.stringify(func.vars), parseOutput: false }); @@ -292,6 +300,7 @@ const deployFunction = async ({ functionId, all, yes } = {}) => { response = await functionsCreateDeployment({ functionId: func['$id'], entrypoint: func.entrypoint, + commands: func.commands, code: func.path, activate: true, parseOutput: false diff --git a/lib/commands/functions.js b/lib/commands/functions.js index 39c566d..5ed1e3f 100644 --- a/lib/commands/functions.js +++ b/lib/commands/functions.js @@ -470,47 +470,38 @@ const functionsCreateDeployment = async ({ functionId, code, activate, entrypoin }); } else { const streamFilePath = payload['code']; - let id = undefined; - let counter = 0; - const totalCounters = Math.ceil(size / libClient.CHUNK_SIZE); - - const headers = { + const apiHeaders = { 'content-type': 'multipart/form-data', }; + let offset = 0; - for (counter; counter < totalCounters; counter++) { - const start = (counter * libClient.CHUNK_SIZE); - const end = Math.min((((counter * libClient.CHUNK_SIZE) + libClient.CHUNK_SIZE) - 1), size); - - headers['content-range'] = 'bytes ' + start + '-' + end + '/' + size; + while (offset < size) { + let end = Math.min(offset + libClient.CHUNK_SIZE - 1, size - 1); - if (id) { - headers['x-appwrite-id'] = id; + apiHeaders['content-range'] = 'bytes ' + offset + '-' + end + '/' + size; + if (response && response.$id) { + apiHeaders['x-appwrite-id'] = response.$id; } const stream = fs.createReadStream(streamFilePath, { - start, + start: offset, end }); payload['code'] = stream; + response = await client.call('post', apiPath, apiHeaders, payload); - response = await client.call('post', apiPath, headers, payload); - - if (!id) { - id = response['$id']; - } - - if (onProgress !== null) { + if (onProgress) { onProgress({ - $id: response['$id'], - progress: Math.min((counter+1) * libClient.CHUNK_SIZE, size) / size * 100, - sizeUploaded: end+1, - chunksTotal: response['chunksTotal'], - chunksUploaded: response['chunksUploaded'] + $id: response.$id, + progress: ( offset / size ) * 100, + sizeUploaded: offset, + chunksTotal: response.chunksTotal, + chunksUploaded: response.chunksUploaded }); } + offset += libClient.CHUNK_SIZE; } } @@ -920,7 +911,7 @@ functions .description(`Update function by its unique ID.`) .requiredOption(`--functionId `, `Function ID.`) .requiredOption(`--name `, `Function name. Max length: 128 chars.`) - .requiredOption(`--runtime `, `Execution runtime.`) + .option(`--runtime `, `Execution runtime.`) .option(`--execute [execute...]`, `An array of role strings with execution permissions. By default no user is granted with any execute permissions. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of 100 roles are allowed, each 64 characters long.`) .option(`--events [events...]`, `Events list. Maximum of 100 events are allowed.`) .option(`--schedule `, `Schedule CRON syntax.`) diff --git a/lib/commands/init.js b/lib/commands/init.js index df8a24d..e91c085 100644 --- a/lib/commands/init.js +++ b/lib/commands/init.js @@ -72,16 +72,22 @@ const initFunction = async () => { log(`Entrypoint for this runtime not found. You will be asked to configure entrypoint when you first deploy the function.`); } + if (!answers.runtime.commands) { + log(`Installation command for this runtime not found. You will be asked to configure the install command when you first deploy the function.`); + } + let response = await functionsCreate({ functionId: answers.id, name: answers.name, runtime: answers.runtime.id, + entrypoint: answers.runtime.entrypoint || '', + commands: answers.runtime.commands || '', parseOutput: false }) fs.mkdirSync(functionDir, "777"); - let gitInitCommands = "git clone --depth 1 --sparse https://github.com/appwrite/functions-starter ."; // depth prevents fetching older commits reducing the amount fetched + let gitInitCommands = "git clone -b v3 --single-branch --depth 1 --sparse https://github.com/appwrite/functions-starter ."; // depth prevents fetching older commits reducing the amount fetched let gitPullCommands = `git sparse-checkout add ${answers.runtime.id}`; @@ -138,13 +144,16 @@ const initFunction = async () => { $id: response['$id'], name: response.name, runtime: response.runtime, - path: `functions/${answers.name}`, - entrypoint: answers.runtime.entrypoint || '', - ignore: answers.runtime.ignore || null, execute: response.execute, events: response.events, schedule: response.schedule, timeout: response.timeout, + enabled: response.enabled, + logging: response.logging, + entrypoint: response.entrypoint, + commands: response.commands, + ignore: answers.runtime.ignore || null, + path: `functions/${answers.name}`, }; localConfig.addFunction(data); diff --git a/lib/commands/storage.js b/lib/commands/storage.js index e5d6355..b6cdadf 100644 --- a/lib/commands/storage.js +++ b/lib/commands/storage.js @@ -300,54 +300,45 @@ const storageCreateFile = async ({ bucketId, fileId, file, permissions, parseOut }, payload) } else { const streamFilePath = payload['file']; - let id = undefined; - let counter = 0; - const totalCounters = Math.ceil(size / libClient.CHUNK_SIZE); - - const headers = { + const apiHeaders = { 'content-type': 'multipart/form-data', }; + let offset = 0; if(fileId != 'unique()') { try { - response = await client.call('get', apiPath + '/' + fileId, headers); - counter = response.chunksUploaded; + response = await client.call('get', apiPath + '/' + fileId, apiHeaders); + offset = response.chunksUploaded * libClient.CHUNK_SIZE; } catch(e) { } } - for (counter; counter < totalCounters; counter++) { - const start = (counter * libClient.CHUNK_SIZE); - const end = Math.min((((counter * libClient.CHUNK_SIZE) + libClient.CHUNK_SIZE) - 1), size); - - headers['content-range'] = 'bytes ' + start + '-' + end + '/' + size; + while (offset < size) { + let end = Math.min(offset + libClient.CHUNK_SIZE - 1, size - 1); - if (id) { - headers['x-appwrite-id'] = id; + apiHeaders['content-range'] = 'bytes ' + offset + '-' + end + '/' + size; + if (response && response.$id) { + apiHeaders['x-appwrite-id'] = response.$id; } const stream = fs.createReadStream(streamFilePath, { - start, + start: offset, end }); payload['file'] = stream; + response = await client.call('post', apiPath, apiHeaders, payload); - response = await client.call('post', apiPath, headers, payload); - - if (!id) { - id = response['$id']; - } - - if (onProgress !== null) { + if (onProgress) { onProgress({ - $id: response['$id'], - progress: Math.min((counter+1) * libClient.CHUNK_SIZE, size) / size * 100, - sizeUploaded: end+1, - chunksTotal: response['chunksTotal'], - chunksUploaded: response['chunksUploaded'] + $id: response.$id, + progress: ( offset / size ) * 100, + sizeUploaded: offset, + chunksTotal: response.chunksTotal, + chunksUploaded: response.chunksUploaded }); } + offset += libClient.CHUNK_SIZE; } } diff --git a/lib/commands/teams.js b/lib/commands/teams.js index ff91bab..665eeb9 100644 --- a/lib/commands/teams.js +++ b/lib/commands/teams.js @@ -193,13 +193,13 @@ const teamsListMemberships = async ({ teamId, queries, search, parseOutput = tru return response; } -const teamsCreateMembership = async ({ teamId, roles, url, email, userId, phone, name, parseOutput = true, sdk = undefined}) => { +const teamsCreateMembership = async ({ teamId, roles, email, userId, phone, url, name, parseOutput = true, sdk = undefined}) => { /* @param {string} teamId */ /* @param {string[]} roles */ - /* @param {string} url */ /* @param {string} email */ /* @param {string} userId */ /* @param {string} phone */ + /* @param {string} url */ /* @param {string} name */ let client = !sdk ? await sdkForProject() : sdk; @@ -447,10 +447,10 @@ teams .description(`Invite a new member to join your team. Provide an ID for existing users, or invite unregistered users using an email or phone number. If initiated from a Client SDK, Appwrite will send an email or sms with a link to join the team to the invited user, and an account will be created for them if one doesn't exist. If initiated from a Server SDK, the new member will be added automatically to the team. You only need to provide one of a user ID, email, or phone number. Appwrite will prioritize accepting the user ID > email > phone number if you provide more than one of these parameters. Use the 'url' parameter to redirect the user from the invitation email to your app. After the user is redirected, use the [Update Team Membership Status](/docs/client/teams#teamsUpdateMembershipStatus) endpoint to allow the user to accept the invitation to the team. Please note that to avoid a [Redirect Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) Appwrite will accept the only redirect URLs under the domains you have added as a platform on the Appwrite Console. `) .requiredOption(`--teamId `, `Team ID.`) .requiredOption(`--roles [roles...]`, `Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Maximum of 100 roles are allowed, each 32 characters long.`) - .requiredOption(`--url `, `URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.`) .option(`--email `, `Email of the new team member.`) .option(`--userId `, `ID of the user to be added to a team.`) .option(`--phone `, `Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.`) + .option(`--url `, `URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.`) .option(`--name `, `Name of the new team member. Max length: 128 chars.`) .action(actionRunner(teamsCreateMembership)) diff --git a/lib/commands/users.js b/lib/commands/users.js index c67ff4a..7cc67f0 100644 --- a/lib/commands/users.js +++ b/lib/commands/users.js @@ -1035,7 +1035,7 @@ users .command(`updateLabels`) .description(`Update the user labels by its unique ID. Labels can be used to grant access to resources. While teams are a way for user's to share access to a resource, labels can be defined by the developer to grant access without an invitation. See the [Permissions docs](/docs/permissions) for more info.`) .requiredOption(`--userId `, `User ID.`) - .requiredOption(`--labels [labels...]`, `Array of user labels. Replaces the previous labels. Maximum of 5 labels are allowed, each up to 36 alphanumeric characters long.`) + .requiredOption(`--labels [labels...]`, `Array of user labels. Replaces the previous labels. Maximum of 100 labels are allowed, each up to 36 alphanumeric characters long.`) .action(actionRunner(usersUpdateLabels)) users diff --git a/lib/parser.js b/lib/parser.js index 45bb574..bbe5287 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -169,6 +169,11 @@ const commandDescriptions = { "login": `The login command allows you to authenticate and manage a user account.`, "logout": `The logout command allows you to logout of your Appwrite account.`, "console" : `The console command allows gives you access to the APIs used by the Appwrite console.`, + "assistant": `The assistant command allows you to interact with the Appwrite Assistant AI`, + "migrations": `The migrations command allows you to migrate data between services.`, + "project": `The project command is for overall project administration.`, + "proxy": `The proxy command allows you to configure behavior for your attached domains.`, + "vcs": `The vcs command allows you to interact with VCS providers and manage your code repositories.`, "main": chalk.redBright(`${logo}${description}`), } diff --git a/lib/questions.js b/lib/questions.js index 70a762a..ed310c9 100644 --- a/lib/questions.js +++ b/lib/questions.js @@ -43,27 +43,57 @@ const getEntrypoint = (runtime) => { case 'dart': return 'lib/main.dart'; case 'deno': - return 'src/mod.ts'; + return 'src/main.ts'; case 'node': - return 'src/index.js'; + return 'src/main.js'; case 'php': return 'src/index.php'; case 'python': - return 'src/index.py'; + return 'src/main.py'; case 'ruby': - return 'src/index.rb'; + return 'lib/main.rb'; case 'rust': return 'main.rs'; case 'swift': - return 'Sources/swift-5.5/main.swift'; + return 'Sources/index.swift'; case 'cpp': - return 'src/index.cc'; + return 'src/main.cc'; case 'dotnet': return 'src/Index.cs'; case 'java': - return 'src/Index.java'; + return 'src/Main.java'; case 'kotlin': - return 'src/Index.kt'; + return 'src/Main.kt'; + } + + return undefined; +}; + +const getInstallCommand = (runtime) => { + const languge = runtime.split('-')[0]; + + switch (languge) { + case 'dart': + return 'dart pub get'; + case 'deno': + return "deno install"; + case 'node': + return 'npm install'; + case 'php': + return 'composer install'; + case 'python': + return 'pip install -r requirements.txt'; + case 'ruby': + return 'bundle install'; + case 'rust': + return 'cargo install'; + case 'dotnet': + return 'dotnet restore'; + case 'swift': + case 'java': + case 'kotlin': + case 'cpp': + return ''; } return undefined; @@ -171,10 +201,15 @@ const questionsInitFunction = [ parseOutput: false }) let runtimes = response["runtimes"] - let choices = runtimes.map((runtime, idx) => { + let choices = runtimes.map((runtime, idx) => { return { name: `${runtime.name} (${runtime['$id']})`, - value: { id: runtime['$id'], entrypoint: getEntrypoint(runtime['$id']), ignore: getIgnores(runtime['$id']) }, + value: { + id: runtime['$id'], + entrypoint: getEntrypoint(runtime['$id']), + ignore: getIgnores(runtime['$id']), + commands : getInstallCommand(runtime['$id']) + }, } }) return choices; diff --git a/package.json b/package.json index 66868d9..2d17366 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "appwrite-cli", "homepage": "https://appwrite.io/support", "description": "Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API", - "version": "3.0.0", + "version": "4.0.0", "license": "BSD-3-Clause", "main": "index.js", "bin": { @@ -22,7 +22,7 @@ "windows-arm64": "pkg -t node16-win-arm64 -o build/appwrite-cli-win-arm64.exe package.json" }, "dependencies": { - "axios": "^0.27.2", + "axios": "1.5.0", "chalk": "4.1.2", "cli-table3": "^0.6.2", "commander": "^9.2.0", diff --git a/scoop/appwrite.json b/scoop/appwrite.json index d5207c9..16299eb 100644 --- a/scoop/appwrite.json +++ b/scoop/appwrite.json @@ -1,12 +1,12 @@ { "$schema": "https://raw.githubusercontent.com/ScoopInstaller/Scoop/master/schema.json", - "version": "3.0.0", + "version": "4.0.0", "description": "The Appwrite CLI is a command-line application that allows you to interact with Appwrite and perform server-side tasks using your terminal.", "homepage": "https://github.com/appwrite/sdk-for-cli", "license": "BSD-3-Clause", "architecture": { "64bit": { - "url": "https://github.com/appwrite/sdk-for-cli/releases/download/3.0.0/appwrite-cli-win-x64.exe", + "url": "https://github.com/appwrite/sdk-for-cli/releases/download/4.0.0/appwrite-cli-win-x64.exe", "bin": [ [ "appwrite-cli-win-x64.exe", @@ -15,7 +15,7 @@ ] }, "arm64": { - "url": "https://github.com/appwrite/sdk-for-cli/releases/download/3.0.0/appwrite-cli-win-arm64.exe", + "url": "https://github.com/appwrite/sdk-for-cli/releases/download/4.0.0/appwrite-cli-win-arm64.exe", "bin": [ [ "appwrite-cli-win-arm64.exe",