From cbc3cb566ce2233dd4b91c97a9fc8289deb8fbd6 Mon Sep 17 00:00:00 2001 From: Gurjeet Singh Virdee <73753957+gurjeetsinghvirdee@users.noreply.github.com> Date: Wed, 10 Sep 2025 23:44:11 +0530 Subject: [PATCH 1/2] Improve error message when authentication is required for project access --- lib/client.js | 221 ++++++++++++++++++++++++++++---------------------- 1 file changed, 126 insertions(+), 95 deletions(-) diff --git a/lib/client.js b/lib/client.js index ca621d05..c0367cf7 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1,4 +1,4 @@ -const os = require('os'); +const os = require("os"); const https = require("https"); const { fetch, FormData, Agent } = require("undici"); const JSONbig = require("json-bigint")({ storeAsString: false }); @@ -7,18 +7,18 @@ const { globalConfig } = require("./config.js"); const chalk = require("chalk"); class Client { - CHUNK_SIZE = 5*1024*1024; // 5MB + CHUNK_SIZE = 5 * 1024 * 1024; // 5MB constructor() { - this.endpoint = 'https://cloud.appwrite.io/v1'; + this.endpoint = "https://cloud.appwrite.io/v1"; this.headers = { - 'content-type': '', - 'x-sdk-name': 'Command Line', - 'x-sdk-platform': 'console', - 'x-sdk-language': 'cli', - 'x-sdk-version': '9.1.0', - 'user-agent' : `AppwriteCLI/9.1.0 (${os.type()} ${os.version()}; ${os.arch()})`, - 'X-Appwrite-Response-Format' : '1.8.0', + "content-type": "", + "x-sdk-name": "Command Line", + "x-sdk-platform": "console", + "x-sdk-language": "cli", + "x-sdk-version": "9.1.0", + "user-agent": `AppwriteCLI/9.1.0 (${os.type()} ${os.version()}; ${os.arch()})`, + "X-Appwrite-Response-Format": "1.8.0", }; } @@ -37,76 +37,76 @@ class Client { return this; } - /** - * Set Project - * - * Your project ID - * - * @param {string} project - * - * @return self - */ - setProject(project) { - this.addHeader('X-Appwrite-Project', project); - - return this; - } + /** + * Set Project + * + * Your project ID + * + * @param {string} project + * + * @return self + */ + setProject(project) { + this.addHeader("X-Appwrite-Project", project); - /** - * Set Key - * - * Your secret API key - * - * @param {string} key - * - * @return self - */ - setKey(key) { - this.addHeader('X-Appwrite-Key', key); - - return this; - } + return this; + } - /** - * Set JWT - * - * Your secret JSON Web Token - * - * @param {string} jwt - * - * @return self - */ - setJWT(jwt) { - this.addHeader('X-Appwrite-JWT', jwt); - - return this; - } + /** + * Set Key + * + * Your secret API key + * + * @param {string} key + * + * @return self + */ + setKey(key) { + this.addHeader("X-Appwrite-Key", key); - /** - * Set Locale - * - * @param {string} locale - * - * @return self - */ - setLocale(locale) { - this.addHeader('X-Appwrite-Locale', locale); - - return this; - } + return this; + } - /** - * Set Mode - * - * @param {string} mode - * - * @return self - */ - setMode(mode) { - this.addHeader('X-Appwrite-Mode', mode); - - return this; - } + /** + * Set JWT + * + * Your secret JSON Web Token + * + * @param {string} jwt + * + * @return self + */ + setJWT(jwt) { + this.addHeader("X-Appwrite-JWT", jwt); + + return this; + } + + /** + * Set Locale + * + * @param {string} locale + * + * @return self + */ + setLocale(locale) { + this.addHeader("X-Appwrite-Locale", locale); + + return this; + } + + /** + * Set Mode + * + * @param {string} mode + * + * @return self + */ + setMode(mode) { + this.addHeader("X-Appwrite-Mode", mode); + + return this; + } /** * Set self signed. @@ -129,8 +129,8 @@ class Client { * @return this */ setEndpoint(endpoint) { - if (!endpoint.startsWith('http://') && !endpoint.startsWith('https://')) { - throw new AppwriteException('Invalid endpoint URL: ' + endpoint); + if (!endpoint.startsWith("http://") && !endpoint.startsWith("https://")) { + throw new AppwriteException("Invalid endpoint URL: " + endpoint); } this.endpoint = endpoint; @@ -147,26 +147,34 @@ class Client { return this; } - async call(method, path = "", headers = {}, params = {}, responseType = "json") { - headers = {...this.headers, ...headers}; + async call( + method, + path = "", + headers = {}, + params = {}, + responseType = "json" + ) { + headers = { ...this.headers, ...headers }; const url = new URL(this.endpoint + path); let body = undefined; if (method.toUpperCase() === "GET") { url.search = new URLSearchParams(Client.flatten(params)).toString(); - } else if (headers["content-type"]?.toLowerCase().startsWith("multipart/form-data")) { + } else if ( + headers["content-type"]?.toLowerCase().startsWith("multipart/form-data") + ) { delete headers["content-type"]; const formData = new FormData(); const flatParams = Client.flatten(params); for (const [key, value] of Object.entries(flatParams)) { - if (value && value.type && value.type === "file") { - formData.append(key, value.file, value.filename); - } else { - formData.append(key, value); - } + if (value && value.type && value.type === "file") { + formData.append(key, value.file, value.filename); + } else { + formData.append(key, value); + } } body = formData; @@ -181,9 +189,9 @@ class Client { headers, body, dispatcher: new Agent({ - connect: { - rejectUnauthorized: !this.selfSigned, - }, + connect: { + rejectUnauthorized: !this.selfSigned, + }, }), }); } catch (error) { @@ -199,11 +207,34 @@ class Client { throw new AppwriteException(text, response.status, "", text); } - if (path !== '/account' && json.code === 401 && json.type === 'user_more_factors_required') { - console.log(`${chalk.cyan.bold("ℹ Info")} ${chalk.cyan("Unusable account found, removing...")}`); + if ( + json.code === 401 && + typeof json.message === "string" && + json.message.includes( + 'User (role: guests) missing scopes (["projects.read"])' + ) + ) { + throw new AppwriteException( + "Cannot access projects. Login is required use 'appwrite login' command", + json.code, + json.type, + text + ); + } + + if ( + path !== "/account" && + json.code === 401 && + json.type === "user_more_factors_required" + ) { + console.log( + `${chalk.cyan.bold("ℹ Info")} ${chalk.cyan( + "Unusable account found, removing..." + )}` + ); const current = globalConfig.getCurrentSession(); - globalConfig.setCurrentSession(''); + globalConfig.setCurrentSession(""); globalConfig.removeSession(current); } throw new AppwriteException(json.message, json.code, json.type, text); @@ -217,7 +248,7 @@ class Client { let cookies = response.headers.getSetCookie(); if (cookies && cookies.length > 0) { for (const cookie of cookies) { - if (cookie.startsWith('a_session_console=')) { + if (cookie.startsWith("a_session_console=")) { globalConfig.setCookie(cookie); } } @@ -233,12 +264,12 @@ class Client { return json; } - static flatten(data, prefix = '') { + static flatten(data, prefix = "") { let output = {}; for (const key in data) { let value = data[key]; - let finalKey = prefix ? prefix + '[' + key +']' : key; + let finalKey = prefix ? prefix + "[" + key + "]" : key; if (Array.isArray(value)) { output = Object.assign(output, Client.flatten(value, finalKey)); // @todo: handle name collision here if needed From 1848aea93bf5e79b0e73850a8d7ef1dfa506529b Mon Sep 17 00:00:00 2001 From: Gurjeet Singh Virdee <73753957+gurjeetsinghvirdee@users.noreply.github.com> Date: Wed, 10 Sep 2025 23:52:14 +0530 Subject: [PATCH 2/2] refactor prettier fix --- lib/client.js | 206 +++++++++++++++++++++++--------------------------- 1 file changed, 95 insertions(+), 111 deletions(-) diff --git a/lib/client.js b/lib/client.js index c0367cf7..117a2815 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1,4 +1,4 @@ -const os = require("os"); +const os = require('os'); const https = require("https"); const { fetch, FormData, Agent } = require("undici"); const JSONbig = require("json-bigint")({ storeAsString: false }); @@ -7,18 +7,18 @@ const { globalConfig } = require("./config.js"); const chalk = require("chalk"); class Client { - CHUNK_SIZE = 5 * 1024 * 1024; // 5MB + CHUNK_SIZE = 5*1024*1024; // 5MB constructor() { - this.endpoint = "https://cloud.appwrite.io/v1"; + this.endpoint = 'https://cloud.appwrite.io/v1'; this.headers = { - "content-type": "", - "x-sdk-name": "Command Line", - "x-sdk-platform": "console", - "x-sdk-language": "cli", - "x-sdk-version": "9.1.0", - "user-agent": `AppwriteCLI/9.1.0 (${os.type()} ${os.version()}; ${os.arch()})`, - "X-Appwrite-Response-Format": "1.8.0", + 'content-type': '', + 'x-sdk-name': 'Command Line', + 'x-sdk-platform': 'console', + 'x-sdk-language': 'cli', + 'x-sdk-version': '9.1.0', + 'user-agent' : `AppwriteCLI/9.1.0 (${os.type()} ${os.version()}; ${os.arch()})`, + 'X-Appwrite-Response-Format' : '1.8.0', }; } @@ -37,76 +37,76 @@ class Client { return this; } - /** - * Set Project - * - * Your project ID - * - * @param {string} project - * - * @return self - */ - setProject(project) { - this.addHeader("X-Appwrite-Project", project); - - return this; - } - - /** - * Set Key - * - * Your secret API key - * - * @param {string} key - * - * @return self - */ - setKey(key) { - this.addHeader("X-Appwrite-Key", key); - - return this; - } - - /** - * Set JWT - * - * Your secret JSON Web Token - * - * @param {string} jwt - * - * @return self - */ - setJWT(jwt) { - this.addHeader("X-Appwrite-JWT", jwt); - - return this; - } + /** + * Set Project + * + * Your project ID + * + * @param {string} project + * + * @return self + */ + setProject(project) { + this.addHeader('X-Appwrite-Project', project); + + return this; + } - /** - * Set Locale - * - * @param {string} locale - * - * @return self - */ - setLocale(locale) { - this.addHeader("X-Appwrite-Locale", locale); + /** + * Set Key + * + * Your secret API key + * + * @param {string} key + * + * @return self + */ + setKey(key) { + this.addHeader('X-Appwrite-Key', key); + + return this; + } - return this; - } + /** + * Set JWT + * + * Your secret JSON Web Token + * + * @param {string} jwt + * + * @return self + */ + setJWT(jwt) { + this.addHeader('X-Appwrite-JWT', jwt); + + return this; + } - /** - * Set Mode - * - * @param {string} mode - * - * @return self - */ - setMode(mode) { - this.addHeader("X-Appwrite-Mode", mode); + /** + * Set Locale + * + * @param {string} locale + * + * @return self + */ + setLocale(locale) { + this.addHeader('X-Appwrite-Locale', locale); + + return this; + } - return this; - } + /** + * Set Mode + * + * @param {string} mode + * + * @return self + */ + setMode(mode) { + this.addHeader('X-Appwrite-Mode', mode); + + return this; + } /** * Set self signed. @@ -129,8 +129,8 @@ class Client { * @return this */ setEndpoint(endpoint) { - if (!endpoint.startsWith("http://") && !endpoint.startsWith("https://")) { - throw new AppwriteException("Invalid endpoint URL: " + endpoint); + if (!endpoint.startsWith('http://') && !endpoint.startsWith('https://')) { + throw new AppwriteException('Invalid endpoint URL: ' + endpoint); } this.endpoint = endpoint; @@ -147,34 +147,26 @@ class Client { return this; } - async call( - method, - path = "", - headers = {}, - params = {}, - responseType = "json" - ) { - headers = { ...this.headers, ...headers }; + async call(method, path = "", headers = {}, params = {}, responseType = "json") { + headers = {...this.headers, ...headers}; const url = new URL(this.endpoint + path); let body = undefined; if (method.toUpperCase() === "GET") { url.search = new URLSearchParams(Client.flatten(params)).toString(); - } else if ( - headers["content-type"]?.toLowerCase().startsWith("multipart/form-data") - ) { + } else if (headers["content-type"]?.toLowerCase().startsWith("multipart/form-data")) { delete headers["content-type"]; const formData = new FormData(); const flatParams = Client.flatten(params); for (const [key, value] of Object.entries(flatParams)) { - if (value && value.type && value.type === "file") { - formData.append(key, value.file, value.filename); - } else { - formData.append(key, value); - } + if (value && value.type && value.type === "file") { + formData.append(key, value.file, value.filename); + } else { + formData.append(key, value); + } } body = formData; @@ -189,9 +181,9 @@ class Client { headers, body, dispatcher: new Agent({ - connect: { - rejectUnauthorized: !this.selfSigned, - }, + connect: { + rejectUnauthorized: !this.selfSigned, + }, }), }); } catch (error) { @@ -222,19 +214,11 @@ class Client { ); } - if ( - path !== "/account" && - json.code === 401 && - json.type === "user_more_factors_required" - ) { - console.log( - `${chalk.cyan.bold("ℹ Info")} ${chalk.cyan( - "Unusable account found, removing..." - )}` - ); + if (path !== '/account' && json.code === 401 && json.type === 'user_more_factors_required') { + console.log(`${chalk.cyan.bold("ℹ Info")} ${chalk.cyan("Unusable account found, removing...")}`); const current = globalConfig.getCurrentSession(); - globalConfig.setCurrentSession(""); + globalConfig.setCurrentSession(''); globalConfig.removeSession(current); } throw new AppwriteException(json.message, json.code, json.type, text); @@ -248,7 +232,7 @@ class Client { let cookies = response.headers.getSetCookie(); if (cookies && cookies.length > 0) { for (const cookie of cookies) { - if (cookie.startsWith("a_session_console=")) { + if (cookie.startsWith('a_session_console=')) { globalConfig.setCookie(cookie); } } @@ -264,12 +248,12 @@ class Client { return json; } - static flatten(data, prefix = "") { + static flatten(data, prefix = '') { let output = {}; for (const key in data) { let value = data[key]; - let finalKey = prefix ? prefix + "[" + key + "]" : key; + let finalKey = prefix ? prefix + '[' + key +']' : key; if (Array.isArray(value)) { output = Object.assign(output, Client.flatten(value, finalKey)); // @todo: handle name collision here if needed