From 9be130f5fc666b1e68173ca28cc89c6184f05183 Mon Sep 17 00:00:00 2001 From: RobertPechaCZ Date: Sat, 30 Aug 2025 18:05:01 +0200 Subject: [PATCH] add changeset --- .changeset/dirty-chairs-drive.md | 5 + src/cli.ts | 2 - src/commands/functions/create.ts | 47 ---- src/commands/functions/delete.ts | 36 --- src/commands/functions/deploy.ts | 227 ----------------- src/commands/functions/index.ts | 107 -------- src/commands/functions/list.ts | 34 --- src/commands/functions/listDeployments.ts | 49 ---- .../prompts/enterFunctionNamePrompt.test.ts | 16 -- .../prompts/enterFunctionNamePrompt.ts | 11 - .../prompts/getFunctionNameOrPrompt.test.ts | 24 -- .../prompts/getFunctionNameOrPrompt.ts | 22 -- .../prompts/getFunctionOrPrompt.test.ts | 94 -------- .../functions/prompts/getFunctionOrPrompt.ts | 36 --- .../prompts/getFunctionPathOrPrompt.test.ts | 26 -- .../prompts/getFunctionPathOrPrompt.ts | 46 ---- .../prompts/getFunctionSlugOrPrompt.test.ts | 11 - .../prompts/getFunctionSlugOrPrompt.ts | 18 -- .../prompts/getFunctionStatusOrPrompt.test.ts | 11 - .../prompts/getFunctionStatusOrPrompt.ts | 17 -- src/commands/functions/prompts/test.js | 1 - .../functions/prompts/testNoPrompt.js | 1 - src/commands/functions/runtimeModules.ts | 55 ----- src/commands/functions/update.ts | 72 ------ .../functions/utils/getJsCodeFromPath.ts | 228 ------------------ .../functions/utils/getWasmCodeFromPath.ts | 91 ------- src/commands/functions/utils/isSiteIdValid.ts | 16 -- .../utils/parseEnvironmentVariables.ts | 79 ------ src/commands/functions/utils/upload.ts | 44 ---- .../functions/utils/uploadFunctionAssets.ts | 38 --- .../functions/wait/waitUntilFileAvailable.ts | 54 ----- src/commands/storage/add.ts | 101 -------- src/commands/storage/delete.test.ts | 210 ---------------- src/commands/storage/delete.ts | 88 ------- src/commands/storage/index.ts | 35 --- 35 files changed, 5 insertions(+), 1947 deletions(-) create mode 100644 .changeset/dirty-chairs-drive.md delete mode 100644 src/commands/functions/create.ts delete mode 100644 src/commands/functions/delete.ts delete mode 100644 src/commands/functions/deploy.ts delete mode 100644 src/commands/functions/index.ts delete mode 100644 src/commands/functions/list.ts delete mode 100644 src/commands/functions/listDeployments.ts delete mode 100644 src/commands/functions/prompts/enterFunctionNamePrompt.test.ts delete mode 100644 src/commands/functions/prompts/enterFunctionNamePrompt.ts delete mode 100644 src/commands/functions/prompts/getFunctionNameOrPrompt.test.ts delete mode 100644 src/commands/functions/prompts/getFunctionNameOrPrompt.ts delete mode 100644 src/commands/functions/prompts/getFunctionOrPrompt.test.ts delete mode 100644 src/commands/functions/prompts/getFunctionOrPrompt.ts delete mode 100644 src/commands/functions/prompts/getFunctionPathOrPrompt.test.ts delete mode 100644 src/commands/functions/prompts/getFunctionPathOrPrompt.ts delete mode 100644 src/commands/functions/prompts/getFunctionSlugOrPrompt.test.ts delete mode 100644 src/commands/functions/prompts/getFunctionSlugOrPrompt.ts delete mode 100644 src/commands/functions/prompts/getFunctionStatusOrPrompt.test.ts delete mode 100644 src/commands/functions/prompts/getFunctionStatusOrPrompt.ts delete mode 100644 src/commands/functions/prompts/test.js delete mode 100644 src/commands/functions/prompts/testNoPrompt.js delete mode 100644 src/commands/functions/runtimeModules.ts delete mode 100644 src/commands/functions/update.ts delete mode 100644 src/commands/functions/utils/getJsCodeFromPath.ts delete mode 100644 src/commands/functions/utils/getWasmCodeFromPath.ts delete mode 100644 src/commands/functions/utils/isSiteIdValid.ts delete mode 100644 src/commands/functions/utils/parseEnvironmentVariables.ts delete mode 100644 src/commands/functions/utils/upload.ts delete mode 100644 src/commands/functions/utils/uploadFunctionAssets.ts delete mode 100644 src/commands/functions/wait/waitUntilFileAvailable.ts delete mode 100644 src/commands/storage/add.ts delete mode 100644 src/commands/storage/delete.test.ts delete mode 100644 src/commands/storage/delete.ts diff --git a/.changeset/dirty-chairs-drive.md b/.changeset/dirty-chairs-drive.md new file mode 100644 index 00000000..e8289732 --- /dev/null +++ b/.changeset/dirty-chairs-drive.md @@ -0,0 +1,5 @@ +--- +"@fleek-platform/cli": major +--- + +Remove Fleek functions commands, remove Fleek storage upload and delete commands diff --git a/src/cli.ts b/src/cli.ts index 8c8f31b3..87a06a3d 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -3,7 +3,6 @@ import cmdApplications from './commands/applications/index'; import cmdAuth from './commands/auth/index'; import cmdDomains from './commands/domains/index'; import cmdEns from './commands/ens/index'; -import cmdFunctions from './commands/functions/index'; import cmdGateways from './commands/gateways/index'; import cmdIPFS from './commands/ipfs/index'; import cmdIPNS from './commands/ipns/index'; @@ -75,7 +74,6 @@ export const init = ({ version, parser }: InitArgs) => { cmdProjects, cmdSites, cmdStorage, - cmdFunctions, cmdVersion, ]; diff --git a/src/commands/functions/create.ts b/src/commands/functions/create.ts deleted file mode 100644 index b1503b78..00000000 --- a/src/commands/functions/create.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { SiteNotFoundError } from '@fleek-platform/errors'; -import { output } from '../../cli'; -import type { SdkGuardedFunction } from '../../guards/types'; -import { withGuards } from '../../guards/withGuards'; -import { t } from '../../utils/translation'; -import { getFunctionNameOrPrompt } from './prompts/getFunctionNameOrPrompt'; -import { isSiteIdValid } from './utils/isSiteIdValid'; - -type CreateFunctionArgs = { - name?: string; - siteId?: string; -}; - -const createAction: SdkGuardedFunction = async ({ - args, - sdk, -}) => { - const { name, siteId } = args; - const functionName = await getFunctionNameOrPrompt({ name }); - - if (siteId && !(await isSiteIdValid({ siteId: siteId as string, sdk }))) { - output.error(t('siteNotFound')); - return; - } - - const newFunction = await sdk.functions().create({ - name: functionName, - siteId: siteId as string, - }); - - output.printNewLine(); - output.success(t('commonNameCreateSuccess', { name: 'function' })); - output.printNewLine(); - - if (!newFunction.currentDeploymentId) { - output.log(t('youCanDoXUsingFolCmd', { action: t('deployNewFunction') })); - output.log('fleek functions deploy'); - } -}; - -export const createActionHandler = withGuards(createAction, { - scopes: { - authenticated: true, - project: true, - site: false, - }, -}); diff --git a/src/commands/functions/delete.ts b/src/commands/functions/delete.ts deleted file mode 100644 index 5ce9d453..00000000 --- a/src/commands/functions/delete.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { output } from '../../cli'; -import type { SdkGuardedFunction } from '../../guards/types'; -import { withGuards } from '../../guards/withGuards'; -import { t } from '../../utils/translation'; -import { getFunctionOrPrompt } from './prompts/getFunctionOrPrompt'; - -type DeleteActionArgs = { - name?: string; -}; - -const deleteAction: SdkGuardedFunction = async ({ - sdk, - args, -}) => { - const functionToDelete = await getFunctionOrPrompt({ name: args.name, sdk }); - - if (!functionToDelete) { - output.error(t('expectedNotFoundGeneric', { name: 'function' })); - - return; - } - - await sdk.functions().delete({ id: functionToDelete.id }); - - output.printNewLine(); - output.success(t('commonNameDeleteSuccess', { name: 'function' })); - output.printNewLine(); -}; - -export const deleteActionHandler = withGuards(deleteAction, { - scopes: { - authenticated: true, - project: true, - site: false, - }, -}); diff --git a/src/commands/functions/deploy.ts b/src/commands/functions/deploy.ts deleted file mode 100644 index 282a13b4..00000000 --- a/src/commands/functions/deploy.ts +++ /dev/null @@ -1,227 +0,0 @@ -import fs from 'node:fs'; -import cliProgress from 'cli-progress'; - -import { output } from '../../cli'; -import type { SdkGuardedFunction } from '../../guards/types'; -import { withGuards } from '../../guards/withGuards'; -import { calculateBlake3Hash } from '../../utils/blake3'; -import { t } from '../../utils/translation'; -import { getFunctionOrPrompt } from './prompts/getFunctionOrPrompt'; -import { getFunctionPathOrPrompt } from './prompts/getFunctionPathOrPrompt'; -import { getJsCodeFromPath } from './utils/getJsCodeFromPath'; -import { getEnvironmentVariables } from './utils/parseEnvironmentVariables'; -import { getUploadResult } from './utils/upload'; -import { waitUntilFileAvailable } from './wait/waitUntilFileAvailable'; - -import { getWasmCodeFromPath } from './utils/getWasmCodeFromPath'; -import { uploadFunctionAssets } from './utils/uploadFunctionAssets'; - -type DeployActionArgs = { - filePath?: string; - name?: string; - bundle: string; - private: boolean; - env: string[]; - envFile?: string; - sgx?: boolean; - assetsPath?: string; -}; - -const deployAction: SdkGuardedFunction = async ({ - sdk, - args, -}) => { - const env = getEnvironmentVariables({ env: args.env, envFile: args.envFile }); - const functionToDeploy = await getFunctionOrPrompt({ name: args.name, sdk }); - const filePath = await getFunctionPathOrPrompt({ path: args.filePath }); - const assetsPath = args.assetsPath; - const bundle = args.bundle !== 'false'; - const isSGX = !!args.sgx; - const isTrustedPrivateEnvironment = isSGX && args.private; - const isUntrustedPublicEnvironment = !isSGX && !args.private; - - if (isTrustedPrivateEnvironment) { - output.error(t('pvtFunctionInSgxNotSupported', { name: 'function' })); - return; - } - - if (!functionToDeploy) { - output.error(t('expectedNotFoundGeneric', { name: 'function' })); - return; - } - - if (assetsPath && isSGX) { - output.error(t('assetsNotSupportedInSgx')); - return; - } - - const assetsCid = await uploadFunctionAssets({ - sdk, - assetsPath, - functionName: functionToDeploy.name, - }); - - const updatedEnv = { - FLEEK_URL: functionToDeploy.invokeUrl, - ...env, - }; - - const filePathToUpload = isSGX - ? await getWasmCodeFromPath({ filePath }) - : await getJsCodeFromPath({ - filePath, - bundle, - env: updatedEnv, - assetsCid, - }); - - output.printNewLine(); - - const progressBar = new cliProgress.SingleBar( - { - format: t('uploadProgress', { action: t('uploadCodeToIpfs') }), - }, - cliProgress.Presets.shades_grey, - ); - - const uploadResult = await getUploadResult({ - filePath: filePathToUpload, - functionName: functionToDeploy.name, - isPrivate: args.private, - progressBar, - sdk, - onFailure: () => { - progressBar.stop(); - }, - }); - - if (!uploadResult) { - output.error( - t('commonFunctionActionFailure', { - action: 'deploy', - tryAgain: t('tryAgain'), - message: t('uploadToIpfsFailed'), - }), - ); - - return; - } - - const blake3Hash = isSGX - ? await calculateBlake3Hash({ - filePath: filePathToUpload, - onFailure: () => { - output.error(t('failedCalculateBlake3Hash')); - process.exit(1); - }, - }) - : undefined; - - if (!output.debugEnabled && !args.bundle) { - fs.rmSync(filePathToUpload); - } - - if (!uploadResult.pin.cid) { - output.error( - t('commonFunctionActionFailure', { - action: 'deploy', - tryAgain: t('tryAgain'), - message: t('uploadToIpfsFailed'), - }), - ); - - return; - } - - if ( - uploadResult.duplicate && - functionToDeploy.currentDeployment && - uploadResult.pin && - functionToDeploy.currentDeployment.cid === uploadResult.pin.cid - ) { - output.chore(t('noChangesDetected')); - - return; - } - - if (!args.private) { - output.printNewLine(); - output.spinner(t('runningAvailabilityCheck')); - - const isAvailable = await waitUntilFileAvailable({ - cid: uploadResult.pin.cid, - }); - - if (!isAvailable) { - output.error(t('availabilityCheckFailed')); - - return; - } - } - - try { - await sdk.functions().deploy({ - functionId: functionToDeploy.id, - cid: uploadResult.pin.cid, - sgx: isSGX, - blake3Hash, - assetsCid, - }); - } catch { - output.error(t('failedDeployFleekFunction')); - process.exit(1); - } - - // TODO: This should probably happen just after uploadResult - // looks more like a post upload process due to propagation - if (isSGX) { - // We need to make a request to the network so the network can have a mapping to the blake3 hash. - // this is a temporarily hack until dalton comes up with a fix on network - // TODO: Check status of supposed fix - output.spinner(t('networkFetchMappings')); - try { - // TODO: The `fleek-test` address should be an env var - await fetch( - `https://fleek-test.network/services/0/ipfs/${uploadResult.pin.cid}`, - ); - } catch { - output.error(t('networkFetchFailed')); - return; - } - } - - output.success(t('commonNameCreateSuccess', { name: 'deployment' })); - output.printNewLine(); - output.log(t('callFleekFunctionByUrlReq')); - output.link(functionToDeploy.invokeUrl); - - if (isSGX) { - output.log(t('callFleekFunctionByNetworkUrlReq')); - output.link('https://fleek-test.network/services/3'); - output.printNewLine(); - output.log(`Blake3 Hash: ${blake3Hash} `); - output.log( - `Invoke by sending request to https://fleek-test.network/services/3 with payload of {hash: , decrypt: true, inputs: "foo"}`, - ); - output.printNewLine(); - output.hint(`Here's an example:`); - output.link( - `curl ${functionToDeploy.invokeUrl} --data '{"hash": "${blake3Hash}", "decrypt": true, "input": "foo"}'`, - ); - } - - if (isUntrustedPublicEnvironment) { - output.log(t('callFleekFunctionByNetworkUrlReq')); - output.link( - `https://fleek-test.network/services/1/ipfs/${uploadResult.pin.cid}`, - ); - } -}; - -export const deployActionHandler = withGuards(deployAction, { - scopes: { - authenticated: true, - project: true, - site: false, - }, -}); diff --git a/src/commands/functions/index.ts b/src/commands/functions/index.ts deleted file mode 100644 index f23776ea..00000000 --- a/src/commands/functions/index.ts +++ /dev/null @@ -1,107 +0,0 @@ -import type { Command } from 'commander'; - -import { t } from '../../utils/translation'; -import { createActionHandler } from './create'; -import { deleteActionHandler } from './delete'; -import { deployActionHandler } from './deploy'; -import { listActionHandler } from './list'; -import { listDeploymentsActionHandler } from './listDeployments'; -import { updateActionHandler } from './update'; - -type DeployOptions = { - path?: string; - name?: string; - bundle: string; - private: boolean; - env?: string[]; - envFile?: string; - sgx?: boolean; - assets?: string; -}; - -export default (program: Command): Command => { - const cmd = program - .command('functions') - .description(t('functionsDescription')); - - cmd - .command('create') - .option('-n, --name ', t('functionName')) - .option('--site ', t('functionsSite')) - .description(t('functionsCreateDescription')) - .action((options: { name?: string; site?: string }) => - createActionHandler({ name: options.name, siteId: options.site }), - ); - - cmd - .command('delete') - .description(t('functionsDeleteDescription')) - .option('-n, --name ', t('functionName')) - .action((options: { name?: string }) => - deleteActionHandler({ name: options.name }), - ); - - cmd - .command('update') - .description(t('functionsUpdateDescription')) - .option('-n, --functionName ', t('functionName')) - .option('--name ', t('functionName')) - .option('--slug ', t('functionSlug')) - .option('--status ', t('functionStatus')) - .action( - (options: { - functionName?: string; - name?: string; - slug?: string; - status?: string; - }) => - updateActionHandler({ - functionName: options.functionName, - name: options.name, - slug: options.slug, - status: options.status, - }), - ); - - cmd - .command('deploy') - .description(t('deployFunction')) - .option('-p, --path ', t('functionCodePath')) - .option('-n, --name ', t('functionName')) - .option('-b, --bundle ', t('bundleCmd'), true) - .option('--private', t('functionDeployToPrivateStorage'), false) - .option('-e, --env ', t('environmentVariables')) - .option('--sgx', t('functionsUseSgx'), false) - .option('-a --assets ', t('functionsUseAssets'), false) - .option( - '--envFile ', - t('environmentVariablesFile'), - ) - .action((options: DeployOptions) => - deployActionHandler({ - filePath: options.path, - name: options.name, - bundle: options.bundle, - private: options.private, - env: options.env ?? [], - envFile: options.envFile, - sgx: options.sgx, - assetsPath: options.assets, - }), - ); - - cmd - .command('list') - .description(t('listFunctionsDesc')) - .action(() => listActionHandler()); - - cmd - .command('deployments') - .option('-n, --name ', t('functionName')) - .description(t('deploymentsListForSelectedFunction')) - .action((options: { name?: string }) => - listDeploymentsActionHandler(options), - ); - - return cmd; -}; diff --git a/src/commands/functions/list.ts b/src/commands/functions/list.ts deleted file mode 100644 index 7e3d7cf8..00000000 --- a/src/commands/functions/list.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { output } from '../../cli'; -import type { SdkGuardedFunction } from '../../guards/types'; -import { withGuards } from '../../guards/withGuards'; -import { t } from '../../utils/translation'; - -const listAction: SdkGuardedFunction = async ({ sdk }) => { - const functions = await sdk.functions().list(); - - if (!functions?.length) { - output.warn(t('noYYet', { name: 'functions' })); - output.log(t('youCanDoXUsingFolCmd', { action: t('createNewFunction') })); - output.log('fleek functions create'); - - return; - } - - output.table( - functions.map((f) => ({ - ID: f.id, - Name: f.name, - Slug: f.slug, - InvokeUrl: f.invokeUrl, - Status: f.status, - })), - ); -}; - -export const listActionHandler = withGuards(listAction, { - scopes: { - authenticated: true, - project: true, - site: false, - }, -}); diff --git a/src/commands/functions/listDeployments.ts b/src/commands/functions/listDeployments.ts deleted file mode 100644 index 0f9c48f8..00000000 --- a/src/commands/functions/listDeployments.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { output } from '../../cli'; -import type { SdkGuardedFunction } from '../../guards/types'; -import { withGuards } from '../../guards/withGuards'; -import { t } from '../../utils/translation'; -import { getFunctionOrPrompt } from './prompts/getFunctionOrPrompt'; - -type ListDeploymentActionArgs = { - name?: string; -}; - -const listDeploymentsAction: SdkGuardedFunction< - ListDeploymentActionArgs -> = async ({ sdk, args }) => { - const functionToList = await getFunctionOrPrompt({ sdk, name: args.name }); - - if (!functionToList) { - output.error(t('expectedNotFoundGeneric', { name: 'function' })); - - return; - } - - const deployments = await sdk - .functions() - .listDeployments({ functionId: functionToList.id }); - - if (!deployments?.length) { - output.warn(t('noYYet', { name: 'deployments' })); - output.log(t('youCanDoXUsingFolCmd', { action: t('deployNewFunction') })); - output.log('fleek functions deploy'); - - return; - } - - output.table( - deployments.map((d) => ({ - ID: d.id, - CID: d.cid, - 'Created At': d.createdAt, - })), - ); -}; - -export const listDeploymentsActionHandler = withGuards(listDeploymentsAction, { - scopes: { - authenticated: true, - project: true, - site: false, - }, -}); diff --git a/src/commands/functions/prompts/enterFunctionNamePrompt.test.ts b/src/commands/functions/prompts/enterFunctionNamePrompt.test.ts deleted file mode 100644 index b4d2d00c..00000000 --- a/src/commands/functions/prompts/enterFunctionNamePrompt.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; - -import { textPrompt } from '../../../prompts/textPrompt'; -import { enterFunctionNamePrompt } from './enterFunctionNamePrompt'; - -vi.mock('../../../prompts/textPrompt', () => ({ - textPrompt: vi.fn().mockResolvedValue('testFunction'), -})); - -describe('Enter function name prompt', () => { - it('returns the correct function name', async () => { - await expect(enterFunctionNamePrompt()).resolves.toEqual('testFunction'); - - expect(textPrompt).toHaveBeenCalledOnce(); - }); -}); diff --git a/src/commands/functions/prompts/enterFunctionNamePrompt.ts b/src/commands/functions/prompts/enterFunctionNamePrompt.ts deleted file mode 100644 index d7b08da6..00000000 --- a/src/commands/functions/prompts/enterFunctionNamePrompt.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { isFunctionNameValid } from '@fleek-platform/utils-validation'; - -import { textPrompt } from '../../../prompts/textPrompt'; -import { t } from '../../../utils/translation'; - -export const enterFunctionNamePrompt = async (): Promise => - textPrompt({ - message: `${t('typeNewFunctionName')}:`, - validate: (partialName: string) => - isFunctionNameValid({ name: partialName }) || t('functionInvalidName'), - }); diff --git a/src/commands/functions/prompts/getFunctionNameOrPrompt.test.ts b/src/commands/functions/prompts/getFunctionNameOrPrompt.test.ts deleted file mode 100644 index bbb88c95..00000000 --- a/src/commands/functions/prompts/getFunctionNameOrPrompt.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; - -import { textPrompt } from '../../../prompts/textPrompt'; -import { getFunctionNameOrPrompt } from './getFunctionNameOrPrompt'; - -vi.mock('../../../prompts/textPrompt', () => ({ - textPrompt: vi.fn().mockResolvedValue('test-function'), -})); - -describe('Get function name', () => { - it('returns the function name', async () => { - await expect( - getFunctionNameOrPrompt({ name: 'test-function' }), - ).resolves.toEqual('test-function'); - }); -}); - -describe('Prompt user for function name', () => { - it('shows text prompt for function name', async () => { - await expect(getFunctionNameOrPrompt({})).resolves.toEqual('test-function'); - - expect(textPrompt).toHaveBeenCalledOnce(); - }); -}); diff --git a/src/commands/functions/prompts/getFunctionNameOrPrompt.ts b/src/commands/functions/prompts/getFunctionNameOrPrompt.ts deleted file mode 100644 index 581e8f24..00000000 --- a/src/commands/functions/prompts/getFunctionNameOrPrompt.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { FleekFunctionNameNotValidError } from '@fleek-platform/errors'; -import { isFunctionNameValid } from '@fleek-platform/utils-validation'; - -import { enterFunctionNamePrompt } from './enterFunctionNamePrompt'; - -type GetFunctionNameOrPromptArgs = { - name?: string; -}; - -export const getFunctionNameOrPrompt = async ({ - name, -}: GetFunctionNameOrPromptArgs): Promise => { - if (name && isFunctionNameValid({ name })) { - return name; - } - - if (name && !isFunctionNameValid({ name })) { - throw new FleekFunctionNameNotValidError({ name }); - } - - return enterFunctionNamePrompt(); -}; diff --git a/src/commands/functions/prompts/getFunctionOrPrompt.test.ts b/src/commands/functions/prompts/getFunctionOrPrompt.test.ts deleted file mode 100644 index 6ccde3de..00000000 --- a/src/commands/functions/prompts/getFunctionOrPrompt.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { FleekFunctionsNotFoundError } from '@fleek-platform/errors'; -import { FleekSdk, PersonalAccessTokenService } from '@fleek-platform/sdk/node'; -import { type Mock, describe, expect, it, vi } from 'vitest'; - -import { selectPrompt } from '../../../prompts/selectPrompt'; -import { getFunctionOrPrompt } from './getFunctionOrPrompt'; - -vi.mock('../../../prompts/selectPrompt', () => ({ - selectPrompt: vi.fn().mockResolvedValue('secondFunctionId'), -})); - -vi.mock('@fleek-platform/sdk/node', () => { - const FleekSdkMock = vi.fn(); - - const functions = { - get: vi.fn().mockResolvedValue({ - name: 'firstFunctionName', - slug: 'first-first-first', - id: 'firstFunctionId', - }), - getBySlug: vi.fn().mockResolvedValue({ - name: 'secondFunctionName', - slug: 'second-second-second', - id: 'secondFunctionId', - }), - list: vi.fn().mockResolvedValue([ - { - name: 'firstFunctionName', - slug: 'first-first-first', - id: 'firstFunctionId', - }, - { - name: 'secondFunctionName', - slug: 'second-second-second', - id: 'secondFunctionId', - }, - ]), - }; - - FleekSdkMock.prototype.functions = () => functions; - - return { FleekSdk: FleekSdkMock, PersonalAccessTokenService: vi.fn() }; -}); - -describe('Get function by name, or let the user choose from list', () => { - it('Return function by its name', async () => { - const accessTokenService = new PersonalAccessTokenService({ - personalAccessToken: '', - }); - const fakeSdk = new FleekSdk({ accessTokenService }); - - await expect( - getFunctionOrPrompt({ sdk: fakeSdk, name: 'firstFunctionName' }), - ).resolves.toEqual({ - name: 'firstFunctionName', - slug: 'first-first-first', - id: 'firstFunctionId', - }); - - expect(fakeSdk.functions().get).toHaveBeenCalledWith({ - name: 'firstFunctionName', - }); - }); - - it('Let the user choose from list and return chosen function', async () => { - const accessTokenService = new PersonalAccessTokenService({ - personalAccessToken: '', - }); - const fakeSdk = new FleekSdk({ accessTokenService }); - - await expect(getFunctionOrPrompt({ sdk: fakeSdk })).resolves.toEqual({ - name: 'secondFunctionName', - slug: 'second-second-second', - id: 'secondFunctionId', - }); - - expect(fakeSdk.functions().list).toHaveBeenCalledOnce(); - expect(selectPrompt).toHaveBeenCalledOnce(); - }); - - it('should throw if no functions exist', async () => { - const accessTokenService = new PersonalAccessTokenService({ - personalAccessToken: '', - }); - const fakeSdk = new FleekSdk({ accessTokenService }); - (fakeSdk.functions().list as Mock).mockResolvedValue([]); - await expect(getFunctionOrPrompt({ sdk: fakeSdk })).rejects.toThrowError( - new FleekFunctionsNotFoundError({}), - ); - - expect(fakeSdk.functions().list).toHaveBeenCalledOnce(); - expect(selectPrompt).not.toHaveBeenCalled(); - }); -}); diff --git a/src/commands/functions/prompts/getFunctionOrPrompt.ts b/src/commands/functions/prompts/getFunctionOrPrompt.ts deleted file mode 100644 index c91767d9..00000000 --- a/src/commands/functions/prompts/getFunctionOrPrompt.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { FleekFunctionsNotFoundError } from '@fleek-platform/errors'; -import type { FleekFunction, FleekSdk } from '@fleek-platform/sdk/node'; - -import { selectPrompt } from '../../../prompts/selectPrompt'; -import { t } from '../../../utils/translation'; - -type GetFunctionOrPromptArgs = { - name?: string; - sdk: FleekSdk; -}; - -export const getFunctionOrPrompt = async ({ - name, - sdk, -}: GetFunctionOrPromptArgs): Promise => { - if (name) { - return sdk.functions().get({ name }); - } - - const functions = await sdk.functions().list(); - - if (!functions.length) { - throw new FleekFunctionsNotFoundError({}); - } - - const selectedFunctionId = await selectPrompt({ - message: t('commonSelectXFromList', { subject: t('function') }), - choices: functions.map((f) => ({ title: f.name, value: f.id })), - }); - - const fnMatch = functions.find((f) => f.id === selectedFunctionId); - - if (!fnMatch) return; - - return fnMatch; -}; diff --git a/src/commands/functions/prompts/getFunctionPathOrPrompt.test.ts b/src/commands/functions/prompts/getFunctionPathOrPrompt.test.ts deleted file mode 100644 index cce48f6b..00000000 --- a/src/commands/functions/prompts/getFunctionPathOrPrompt.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; - -import { textPrompt } from '../../../prompts/textPrompt'; -import { getFunctionPathOrPrompt } from './getFunctionPathOrPrompt'; - -vi.mock('../../../prompts/textPrompt', () => ({ - textPrompt: vi.fn().mockResolvedValue(`${__dirname}/test.js`), -})); - -describe('Get function code', () => { - it('returns the function code', async () => { - await expect( - getFunctionPathOrPrompt({ path: `${__dirname}/testNoPrompt.js` }), - ).resolves.toStrictEqual(`${__dirname}/testNoPrompt.js`); - }); -}); - -describe('Prompt user for function code path', () => { - it('shows text prompt for function code', async () => { - await expect(getFunctionPathOrPrompt({})).resolves.toStrictEqual( - `${__dirname}/test.js`, - ); - - expect(textPrompt).toHaveBeenCalledOnce(); - }); -}); diff --git a/src/commands/functions/prompts/getFunctionPathOrPrompt.ts b/src/commands/functions/prompts/getFunctionPathOrPrompt.ts deleted file mode 100644 index e34ff547..00000000 --- a/src/commands/functions/prompts/getFunctionPathOrPrompt.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { FleekFunctionPathNotValidError } from '@fleek-platform/errors'; -import { - isFunctionPathValid, - isValidFolder, -} from '@fleek-platform/utils-validation'; - -import { textPrompt } from '../../../prompts/textPrompt'; -import { t } from '../../../utils/translation'; - -type GetFunctionPathOrPromptArgs = { - path?: string; -}; - -const isValidPath = async (path: string) => - await isFunctionPathValid({ fileOrFolderPath: path }); - -export const getFunctionPathOrPrompt = async ({ - path, -}: GetFunctionPathOrPromptArgs): Promise => { - let result = path; - - if (!result) { - const p = await textPrompt({ - message: t('typeFunctionCodePath'), - validate: (path) => - isFunctionPathValid({ fileOrFolderPath: path }) || - t('filePathValidWarning'), - }); - - result = p; - } - - const hasValidPath = await isValidPath(result); - - if (!hasValidPath) { - throw new FleekFunctionPathNotValidError({ path: result }); - } - - const isFolder = await isValidFolder(result); - - if (isFolder) { - return `${result}/index.js`; - } - - return result; -}; diff --git a/src/commands/functions/prompts/getFunctionSlugOrPrompt.test.ts b/src/commands/functions/prompts/getFunctionSlugOrPrompt.test.ts deleted file mode 100644 index 571da454..00000000 --- a/src/commands/functions/prompts/getFunctionSlugOrPrompt.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { getFunctionSlugOrPrompt } from './getFunctionSlugOrPrompt'; - -describe('Get function slug', () => { - it('returns the function slug', async () => { - await expect( - getFunctionSlugOrPrompt({ slug: 'test-slug' }), - ).resolves.toEqual('test-slug'); - }); -}); diff --git a/src/commands/functions/prompts/getFunctionSlugOrPrompt.ts b/src/commands/functions/prompts/getFunctionSlugOrPrompt.ts deleted file mode 100644 index bb2dcb1b..00000000 --- a/src/commands/functions/prompts/getFunctionSlugOrPrompt.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { FleekFunctionSlugNotValidError } from '@fleek-platform/errors'; -import { isFunctionSlugValid } from '@fleek-platform/utils-validation'; - -type GetFunctionSlugOrPromptArgs = { - slug?: string; -}; - -export const getFunctionSlugOrPrompt = async ({ - slug, -}: GetFunctionSlugOrPromptArgs): Promise => { - if (slug && isFunctionSlugValid({ slug })) { - return slug; - } - - if (!slug) return; - - throw new FleekFunctionSlugNotValidError({ slug }); -}; diff --git a/src/commands/functions/prompts/getFunctionStatusOrPrompt.test.ts b/src/commands/functions/prompts/getFunctionStatusOrPrompt.test.ts deleted file mode 100644 index f8685478..00000000 --- a/src/commands/functions/prompts/getFunctionStatusOrPrompt.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { getFunctionStatusOrPrompt } from './getFunctionStatusOrPrompt'; - -describe('Get function status', () => { - it('returns the function status', async () => { - await expect( - getFunctionStatusOrPrompt({ status: 'ACTIVE' }), - ).resolves.toEqual('ACTIVE'); - }); -}); diff --git a/src/commands/functions/prompts/getFunctionStatusOrPrompt.ts b/src/commands/functions/prompts/getFunctionStatusOrPrompt.ts deleted file mode 100644 index 778ec0f4..00000000 --- a/src/commands/functions/prompts/getFunctionStatusOrPrompt.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { FleekFunctionStatusNotValidError } from '@fleek-platform/errors'; -import type { FleekFunctionStatus } from '@fleek-platform/sdk/node'; -import { isFunctionStatusValid } from '@fleek-platform/utils-validation'; - -type GetFunctionStatusOrPromptArgs = { - status?: string; -}; - -export const getFunctionStatusOrPrompt = async ({ - status, -}: GetFunctionStatusOrPromptArgs): Promise => { - if (status && isFunctionStatusValid({ status })) { - return status as FleekFunctionStatus; - } - - throw new FleekFunctionStatusNotValidError({}); -}; diff --git a/src/commands/functions/prompts/test.js b/src/commands/functions/prompts/test.js deleted file mode 100644 index 5a2693db..00000000 --- a/src/commands/functions/prompts/test.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = () => 'hello world'; diff --git a/src/commands/functions/prompts/testNoPrompt.js b/src/commands/functions/prompts/testNoPrompt.js deleted file mode 100644 index 5a2693db..00000000 --- a/src/commands/functions/prompts/testNoPrompt.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = () => 'hello world'; diff --git a/src/commands/functions/runtimeModules.ts b/src/commands/functions/runtimeModules.ts deleted file mode 100644 index c1fa5a6a..00000000 --- a/src/commands/functions/runtimeModules.ts +++ /dev/null @@ -1,55 +0,0 @@ -// Worker supported modules -// due to environment constraints -// optimized for edge computing -export const supportedRuntimeModules = [ - 'buffer', - 'crypto', - 'domain', - 'events', - 'http', - 'https', - 'path', - 'punycode', - 'stream', - 'string_decoder', - 'url', - 'util', - 'zlib', -]; - -export const unsupportedRuntimeModules = [ - 'assert/strict', - 'child_process', - 'cluster', - 'constants', - 'dgram', - 'diagnostics_channel', - 'dns', - 'fs', - 'fs/promises', - 'http2', - 'inspector', - 'module', - 'net', - 'os', - 'path/posix', - 'path/win32', - 'perf_hooks', - 'process', - 'querystring', - 'readline', - 'repl', - 'stream/promises', - 'stream/web', - 'sys', - 'timers', - 'timers/promises', - 'tls', - 'trace_events', - 'tty', - 'v8', - 'vm', - 'wasi', - 'webcrypto', - 'worker_threads', -]; diff --git a/src/commands/functions/update.ts b/src/commands/functions/update.ts deleted file mode 100644 index 31937cc4..00000000 --- a/src/commands/functions/update.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { output } from '../../cli'; -import type { SdkGuardedFunction } from '../../guards/types'; -import { withGuards } from '../../guards/withGuards'; -import { t } from '../../utils/translation'; -import { getFunctionNameOrPrompt } from './prompts/getFunctionNameOrPrompt'; -import { getFunctionOrPrompt } from './prompts/getFunctionOrPrompt'; -import { getFunctionSlugOrPrompt } from './prompts/getFunctionSlugOrPrompt'; -import { getFunctionStatusOrPrompt } from './prompts/getFunctionStatusOrPrompt'; - -type UpdateFunctionArgs = { - functionName?: string; - name?: string; - slug?: string; - status?: string; -}; - -const updateAction: SdkGuardedFunction = async ({ - args, - sdk, -}) => { - if (!args.name && !args.slug && !args.status) { - output.error( - t('functionUpdateArgsNotValid', { - param1: 'name', - param2: 'slug', - param3: 'status', - }), - ); - - return; - } - - const name = args.name - ? await getFunctionNameOrPrompt({ name: args.name }) - : undefined; - const slug = args.slug - ? await getFunctionSlugOrPrompt({ slug: args.slug }) - : undefined; - const status = args.status - ? await getFunctionStatusOrPrompt({ status: args.status }) - : undefined; - - const fleekFunction = await getFunctionOrPrompt({ - name: args.functionName, - sdk, - }); - - if (!fleekFunction) { - output.error(t('expectedNotFoundGeneric', { name: 'function' })); - - return; - } - - await sdk.functions().update({ id: fleekFunction.id, slug, status, name }); - - output.printNewLine(); - output.success( - t('commonItemActionSuccess', { - subject: t('function'), - action: t('updated'), - }), - ); - output.printNewLine(); -}; - -export const updateActionHandler = withGuards(updateAction, { - scopes: { - authenticated: true, - project: true, - site: false, - }, -}); diff --git a/src/commands/functions/utils/getJsCodeFromPath.ts b/src/commands/functions/utils/getJsCodeFromPath.ts deleted file mode 100644 index 039bb201..00000000 --- a/src/commands/functions/utils/getJsCodeFromPath.ts +++ /dev/null @@ -1,228 +0,0 @@ -import * as fs from 'node:fs'; -import * as os from 'node:os'; - -// TODO: These error messages should be revised -// e.g. FleekFunctionPathNotValidError happens regardless of bundling -import { - FleekFunctionBundlingFailedError, - FleekFunctionPathNotValidError, - UnknownError, -} from '@fleek-platform/errors'; -import cliProgress from 'cli-progress'; -import { type Plugin, type PluginBuild, build } from 'esbuild'; -import { filesFromPaths } from 'files-from-path'; - -import { output } from '../../../cli'; -import { t } from '../../../utils/translation'; -import type { EnvironmentVariables } from './parseEnvironmentVariables'; - -type TranspileResponse = { - path: string; - unsupportedModules: Set; - success: boolean; - error?: string; -}; - -type ShowUnsupportedModulesArgs = { - unsupportedModulesUsed: Set; -}; - -const showUnsupportedModules = (args: ShowUnsupportedModulesArgs) => { - const unsupportedModulesUsed = Array.from(args.unsupportedModulesUsed); - - if (unsupportedModulesUsed.length) { - output.printNewLine(); - for (const packageName of unsupportedModulesUsed) { - output.mistake(t('unsupportedPackage', { packageName })); - } - - output.log(t('showUnsupportedModulesDocLink')); - output.link('https://fleek.xyz/docs'); - output.printNewLine(); - } -}; - -const buildEnvVars = (args: { env: EnvironmentVariables }) => { - return Object.entries(args.env) - .map(([key, value]) => `${key}: "${value}"`) - .join(','); -}; - -type TranspileCodeArgs = { - filePath: string; - bundle: boolean; - env: EnvironmentVariables; - assetsCid?: string; -}; - -const transpileCode = async (args: TranspileCodeArgs) => { - const { - createFleekBuildConfig, - nodeProtocolImportSpecifier, - moduleChecker, - unsupportedRuntimeModules, - } = await import('@fleek-platform/functions-esbuild-config'); - - const { filePath, bundle, env, assetsCid } = args; - const progressBar = new cliProgress.SingleBar( - { - format: t('uploadProgress', { - action: t(bundle ? 'bundlingCode' : 'transformingCode'), - }), - }, - cliProgress.Presets.shades_grey, - ); - - let tempDir: string; - - if (!output.debugEnabled) { - tempDir = os.tmpdir(); - } else { - tempDir = '.fleek'; - - if (!fs.existsSync(tempDir)) { - fs.mkdirSync(tempDir); - } - } - - const outFile = `${tempDir}/function.js`; - const unsupportedModulesUsed = new Set(); - - const plugins: Plugin[] = [ - moduleChecker({ - unsupportedModulesUsed: new Set(unsupportedRuntimeModules), - }), - { - name: 'ProgressBar', - setup: (build: PluginBuild) => { - build.onStart(() => { - progressBar.start(100, 10); - }); - }, - }, - ]; - - if (bundle) { - plugins.push( - nodeProtocolImportSpecifier({ - // Handle the error gracefully - onError: () => output.error(t('failedToApplyNodeImportProtocol')), - }), - ); - } - - let adaptedEnv = env; - if (assetsCid) { - adaptedEnv = { - ...env, - ASSETS_CID: assetsCid, - }; - } - - const buildConfig = createFleekBuildConfig({ - filePath, - bundle, - env: adaptedEnv, - }); - - try { - await build({ - ...buildConfig, - outfile: outFile, - plugins, - minify: !!bundle, - }); - - progressBar.update(100); - progressBar.stop(); - } catch (e) { - progressBar.stop(); - - const errorMessage = - e && - typeof e === 'object' && - 'message' in e && - typeof e.message === 'string' - ? e.message - : t('unknownTransformError'); - - const transpileResponse: TranspileResponse = { - path: filePath, - unsupportedModules: unsupportedModulesUsed, - success: false, - error: errorMessage, - }; - - return transpileResponse; - } - - const transpileResponse: TranspileResponse = { - path: outFile, - unsupportedModules: unsupportedModulesUsed, - success: true, - }; - - return transpileResponse; -}; - -export const getFileLikeObject = async (path: string) => { - const files = await filesFromPaths([path]); - - if (!files.length) { - throw new FleekFunctionPathNotValidError({ path }); - } - - return files[0]; -}; - -// TODO: Create a process to validate the user source code -// using placeholder for the moment -const checkUserSourceCodeSupport = async (filePath: string) => { - const reRequireSyntax = /require\s*\([^)]*\)/g; - const buffer = await fs.promises.readFile(filePath); - const contents = buffer.toString(); - - return reRequireSyntax.test(contents); -}; - -export const getJsCodeFromPath = async (args: { - filePath: string; - bundle: boolean; - env: EnvironmentVariables; - assetsCid?: string; -}) => { - const { filePath, bundle, env, assetsCid } = args; - - if (!fs.existsSync(filePath)) { - throw new FleekFunctionPathNotValidError({ path: filePath }); - } - - const isUserSourceCodeSupported = await checkUserSourceCodeSupport(filePath); - - if (isUserSourceCodeSupported) { - output.error(t('requireDeprecatedUseES6Syntax')); - } - - const transpileResponse = await transpileCode({ - filePath, - bundle, - env, - assetsCid, - }); - - showUnsupportedModules({ - unsupportedModulesUsed: transpileResponse.unsupportedModules, - }); - - if (!transpileResponse.success) { - if (!transpileResponse.error) { - throw new UnknownError(); - } - - throw new FleekFunctionBundlingFailedError({ - error: transpileResponse.error, - }); - } - - return transpileResponse.path; -}; diff --git a/src/commands/functions/utils/getWasmCodeFromPath.ts b/src/commands/functions/utils/getWasmCodeFromPath.ts deleted file mode 100644 index b233e37f..00000000 --- a/src/commands/functions/utils/getWasmCodeFromPath.ts +++ /dev/null @@ -1,91 +0,0 @@ -import * as fs from 'node:fs'; -import * as os from 'node:os'; - -import { - FleekFunctionInvalidWasmCodeError, - FleekFunctionPathNotValidError, - FleekFunctionWasmEncryptionFailedError, -} from '@fleek-platform/errors'; -import cliProgress from 'cli-progress'; -import { encrypt } from 'eciesjs'; -import { output } from '../../../cli'; -import { t } from '../../../utils/translation'; - -const PUBLIC_KEY = - '02de6500ea852d2f4bdc9b6812ac76477e45eae556998d357cfa84e5a0a71bddb4'; - -const getWasm = async (filePath: string) => { - const buffer = await fs.promises.readFile(filePath); - if (buffer.length < 8) { - return null; - } - - // WebAssembly namespace is only supported in dom in typescript - // https://webassembly.github.io/spec/core/binary/modules.html#binary-module - const wasmMagicNumber = [0x00, 0x61, 0x73, 0x6d]; - for (let i = 0; i < 4; i++) { - if (buffer[i] !== wasmMagicNumber[i]) { - return null; - } - } - return buffer; -}; - -const enryptCode = async (args: { filePath: string }) => { - const { filePath } = args; - - const buffer = await getWasm(filePath); - if (!buffer) { - output.error(t('invalidWasmCode', { path: filePath })); - throw new FleekFunctionInvalidWasmCodeError({}); - } - - const progressBar = new cliProgress.SingleBar( - { - format: t('uploadProgress', { - action: t('encryptingCode'), - }), - }, - cliProgress.Presets.shades_grey, - ); - - let tempDir: string; - - if (!output.debugEnabled) { - tempDir = os.tmpdir(); - } else { - tempDir = '.fleek'; - - if (!fs.existsSync(tempDir)) { - fs.mkdirSync(tempDir); - } - } - const outFile = `${tempDir}/function.wasm`; - - progressBar.start(100, 10); - try { - const encryptedData = encrypt(PUBLIC_KEY, buffer); - progressBar.update(50); - - await fs.promises.writeFile(outFile, encryptedData); - } catch (error) { - progressBar.stop(); - throw new FleekFunctionWasmEncryptionFailedError({}); - } - progressBar.update(100); - progressBar.stop(); - - return outFile; -}; - -export const getWasmCodeFromPath = async (args: { - filePath: string; -}) => { - const { filePath } = args; - - if (!fs.existsSync(filePath)) { - throw new FleekFunctionPathNotValidError({ path: filePath }); - } - - return enryptCode({ filePath }); -}; diff --git a/src/commands/functions/utils/isSiteIdValid.ts b/src/commands/functions/utils/isSiteIdValid.ts deleted file mode 100644 index f01e2ea2..00000000 --- a/src/commands/functions/utils/isSiteIdValid.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { FleekSdk } from '@fleek-platform/sdk/node'; - -export const isSiteIdValid = async ({ - siteId, - sdk, -}: { - siteId: string; - sdk: FleekSdk; -}) => { - try { - await sdk.sites().get({ id: siteId }); - return true; - } catch { - return false; - } -}; diff --git a/src/commands/functions/utils/parseEnvironmentVariables.ts b/src/commands/functions/utils/parseEnvironmentVariables.ts deleted file mode 100644 index 81b20ea7..00000000 --- a/src/commands/functions/utils/parseEnvironmentVariables.ts +++ /dev/null @@ -1,79 +0,0 @@ -import fs from 'node:fs'; -import dotenv from 'dotenv'; - -import { output } from '../../../cli'; -import { t } from '../../../utils/translation'; - -export type EnvironmentVariables = { [key: string]: string }; - -export type ParseEnvironmentVariablesArgs = { env: string[] }; -export type ParseEnvironmentVariablesFileArgs = { envFile: string }; -export type GetEnvironmentVariablesArgs = { env: string[]; envFile?: string }; - -export const parseEnvironmentVariablesFile = ( - args: ParseEnvironmentVariablesFileArgs, -): EnvironmentVariables => { - const { envFile } = args; - - if (!fs.statSync(envFile).isFile()) { - output.mistake(t('filePathNotFound', { envFile })); - - return {}; - } - - try { - const envFileContent = fs.readFileSync(envFile); - const config = dotenv.parse(envFileContent); - - return config; - } catch (err) { - output.mistake(t('envFileParseError', { envFile })); - - return {}; - } -}; - -export const parseEnvironmentVariables = ( - args: ParseEnvironmentVariablesArgs, -): EnvironmentVariables => { - const { env } = args; - - return env.reduce<{ [key: string]: string }>((acc, curr) => { - const [key, value] = curr.split('='); - - let varValue = value; - - if (!varValue) { - // eslint-disable-next-line no-process-env - const envValue = process.env[key]; - - if (!envValue) { - output.mistake(t('missingEnvVar', { key })); - - return acc; - } - - varValue = envValue; - } - - acc[key] = varValue; - - return acc; - }, {}); -}; - -export const getEnvironmentVariables = ( - args: GetEnvironmentVariablesArgs, -): EnvironmentVariables => { - const { env, envFile } = args; - - const environmentVariables = parseEnvironmentVariables({ env }); - - let envFileContent: EnvironmentVariables = {}; - - if (envFile) { - envFileContent = parseEnvironmentVariablesFile({ envFile }); - } - - return { ...envFileContent, ...environmentVariables }; -}; diff --git a/src/commands/functions/utils/upload.ts b/src/commands/functions/utils/upload.ts deleted file mode 100644 index cfcbd596..00000000 --- a/src/commands/functions/utils/upload.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { uploadOnProgress } from '../../../output/utils/uploadOnProgress'; -import { getFileLikeObject } from './getJsCodeFromPath'; - -import type { FleekSdk } from '@fleek-platform/sdk/node'; -import type { Bar as ProgressBar } from 'cli-progress'; -import type { FileLike } from '../../storage/utils/upload'; - -export const getUploadResult = async ({ - filePath, - functionName, - isPrivate, - progressBar, - sdk, - onFailure, -}: { - filePath: string; - functionName: string; - isPrivate: boolean; - progressBar: ProgressBar; - sdk: FleekSdk; - onFailure?: () => void; -}) => { - try { - if (isPrivate) { - return await sdk.storage().uploadPrivateFile({ - filePath, - onUploadProgress: uploadOnProgress(progressBar), - }); - } - - const fileLikeObject = (await getFileLikeObject(filePath)) as FileLike; - return await sdk.storage().uploadFile({ - file: fileLikeObject, - options: { functionName }, - onUploadProgress: uploadOnProgress(progressBar), - }); - } catch { - if (typeof onFailure === 'function') { - onFailure(); - } - } - - return; -}; diff --git a/src/commands/functions/utils/uploadFunctionAssets.ts b/src/commands/functions/utils/uploadFunctionAssets.ts deleted file mode 100644 index e2e854fb..00000000 --- a/src/commands/functions/utils/uploadFunctionAssets.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { FleekSdk } from '@fleek-platform/sdk/node'; -import { isValidFolder } from '@fleek-platform/utils-validation'; -import { output } from '../../../cli'; -import { t } from '../../../utils/translation'; - -export const uploadFunctionAssets = async ({ - sdk, - assetsPath, - functionName, -}: { - sdk: FleekSdk; - functionName: string; - assetsPath?: string; -}): Promise => { - if (!assetsPath) { - return; - } - - if (!(await isValidFolder(assetsPath))) { - output.error(t('assetsPathIsNotAFolder')); - return; - } - - try { - output.spinner(t('uploadingAssets')); - const result = await sdk.storage().uploadDirectory({ - path: assetsPath, - options: { - functionName, - }, - }); - output.success(t('assetsUploadSuccess')); - return result.pin.cid; - } catch (error) { - output.error(t('uploadAssetsFailed')); - throw error; - } -}; diff --git a/src/commands/functions/wait/waitUntilFileAvailable.ts b/src/commands/functions/wait/waitUntilFileAvailable.ts deleted file mode 100644 index 62f65860..00000000 --- a/src/commands/functions/wait/waitUntilFileAvailable.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { checkPeriodicallyUntil } from '../../../utils/checkPeriodicallyUntil'; - -type WaitUntilFileAvailableArgs = { - cid: string; -}; - -export const waitUntilFileAvailable = async ({ - cid, -}: WaitUntilFileAvailableArgs) => { - const timeout = 10000; - const gatewayPatterns = [ - 'https://{cid}.ipfs.dweb.link', - 'https://{cid}.ipfs.w3s.link', - 'https://{cid}.ipfs.flk-ipfs.xyz', - 'https://ipfs.io/ipfs/{cid}', - 'https://fleek.ipfs.io/ipfs/{cid}', - ]; - const createUrlPromises = () => - gatewayPatterns.map((pattern: string) => { - const url = pattern.replace(/\{(\w+)\}/g, cid); - const fetchPromise = fetch(url).then((response) => { - if (!response.ok) { - // eslint-disable-next-line fleek-custom/no-default-error - throw new Error(`Request failed for ${url}`); - } - - return response; - }); - - const timeoutPromise = new Promise((_, reject) => - setTimeout( - () => reject(new Error(`timeout fetching content from ${url}`)), - timeout, - ), - ); - - return Promise.race([fetchPromise, timeoutPromise]); - }); - - return checkPeriodicallyUntil({ - conditionFn: async () => { - const urlPromises = createUrlPromises(); - try { - await Promise.any(urlPromises); - - return true; - } catch { - return false; - } - }, - period: 6000, - tries: 50, - }); -}; diff --git a/src/commands/storage/add.ts b/src/commands/storage/add.ts deleted file mode 100644 index 29729d3e..00000000 --- a/src/commands/storage/add.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { promises as fs, existsSync } from 'node:fs'; -import { basename } from 'node:path'; - -import { - getPrivateIpfsGatewayUrl, - getWeb3IpfsGatewayUrl, -} from '@fleek-platform/utils-ipfs'; -import cliProgress from 'cli-progress'; -import { filesFromPaths } from 'files-from-path'; - -import { output } from '../../cli'; -import type { SdkGuardedFunction } from '../../guards/types'; -import { withGuards } from '../../guards/withGuards'; -import { t } from '../../utils/translation'; -import { getAllActivePrivateGatewayDomains } from '../gateways/utils/getAllPrivateGatewayDomains'; -import type { FileLike } from './utils/upload'; -import { uploadStorage } from './utils/upload'; - -type AddStorageActionArgs = { - path: string; -}; - -export const addStorageAction: SdkGuardedFunction< - AddStorageActionArgs -> = async ({ sdk, args }) => { - if (!existsSync(args.path)) { - output.error(t('filePathNotFound', { path: args.path })); - - return; - } - - const progressBar = new cliProgress.SingleBar( - { - format: - 'Upload Progress [{bar}] {percentage}% | ETA: {eta}s | {value}/{total}', - }, - cliProgress.Presets.shades_grey, - ); - const directoryName = basename(args.path); - const files: FileLike[] = await filesFromPaths([args.path]); - - const storage = await uploadStorage({ - path: args.path, - sdk, - files, - directoryName, - progressBar, - onFailure: () => { - progressBar.stop(); - }, - }); - - if (!storage) { - output.error(t('somethingWrongDurUpload')); - - return; - } - - const hash = storage?.pin.cid.toString(); - - if (storage.duplicate) { - output.warn(t('fileAlreadyExistWarn', { path: args.path })); - - output.printNewLine(); - } else { - output.success(t('storageUploadSuccessCid', { cid: hash })); - output.printNewLine(); - } - - const privateGatewayDomains = await getAllActivePrivateGatewayDomains({ - sdk, - }); - - if (privateGatewayDomains.length === 0) { - output.log(t('visitViaGateway')); - output.link(getWeb3IpfsGatewayUrl(hash)); - - return; - } - - output.log(t('visitViaPvtGw')); - - for (const privateGatewayDomain of privateGatewayDomains) { - output.link( - getPrivateIpfsGatewayUrl({ - hostname: privateGatewayDomain.hostname, - hash, - }), - ); - } - - output.printNewLine(); -}; - -export const addStorageActionHandler = withGuards(addStorageAction, { - scopes: { - authenticated: true, - project: true, - site: false, - }, -}); diff --git a/src/commands/storage/delete.test.ts b/src/commands/storage/delete.test.ts deleted file mode 100644 index f9dafec0..00000000 --- a/src/commands/storage/delete.test.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { FleekSdk, PersonalAccessTokenService } from '@fleek-platform/sdk/node'; -import { describe, expect, it, vi } from 'vitest'; - -import { output } from '../../cli'; -import { deleteStorageAction } from './delete'; - -vi.mock('../../cli', () => { - const output = { - log: vi.fn(), - success: vi.fn(), - error: vi.fn(), - }; - - return { output }; -}); - -vi.mock('@fleek-platform/sdk/node', () => { - const FleekSdkMock = vi.fn(); - - const storage = { - delete: vi.fn((options: { cid: string }) => { - if ( - options.cid === - 'bafkreiebwzjtd62ctklmmidldy2z2exinzr2mc72tzhkbe7ftjxm7cwnle' - ) { - return Promise.resolve({ body: {}, status: 500 }); - } - if (options.cid.startsWith('bafyunauth')) { - return Promise.resolve({ body: {}, status: 401 }); - } - - return Promise.resolve({ body: { deleted: true }, status: 200 }); - }), - getByFilename: vi.fn((options: { filename: string; extension: string }) => { - const batchProcessingData = [...Array(25).keys()].map((i) => ({ - filename: 'basicBatch.car', - cid: `bafyrandom${i}`, - })); - const batchProcessingUnauthorized = [...Array(15).keys()].map((i) => ({ - filename: 'basicBatchUnauthorized.car', - cid: `bafyunauth${i}`, - })); - const data = [ - { - filename: 'basic.car', - cid: 'bafybeifylyzjlrpec75l66kggycx65yuouyavweaaqxmf22jvbtnmmaqru', - filecoinDealIds: '58027558', - arweaveId: '', - }, - { - filename: 'basic.car', - cid: 'bafkreieasoapp3osmpdt2lwdqy6oqx75nhdsxgkoswyjuwy2675eyhvcg4', - filecoinDealIds: '61342544', - arweaveId: '', - }, - { - filename: 'basic1Fail.car', - cid: 'bafkreiebwzjtd62ctklmmidldy2z2exinzr2mc72tzhkbe7ftjxm7cwnle', - filecoinDealIds: '61342549', - arweaveId: '', - }, - { - filename: 'basic1Fail.car', - cid: 'bafkreidlixt2jjjsdxxdzgv3spe46mcwtpsmlxgumpsyaru6n6fn3jz2zy', - filecoinDealIds: '61342999', - arweaveId: '', - }, - ...batchProcessingData, - ...batchProcessingUnauthorized, - ]; - const filtered = data.filter( - (item) => - [options.filename, options.extension].join('.') === item.filename, - ); - - return Promise.resolve(filtered.length > 0 ? filtered : undefined); - }), - }; - - FleekSdkMock.prototype.storage = () => storage; - - return { FleekSdk: FleekSdkMock, PersonalAccessTokenService: vi.fn() }; -}); - -describe('Delete storage files/folder for the given cid or name', () => { - it('should delete storage by name', async () => { - const accessTokenService = new PersonalAccessTokenService({ - personalAccessToken: '', - }); - const fakeSdk = new FleekSdk({ accessTokenService }); - - await expect( - deleteStorageAction({ - sdk: fakeSdk, - args: { - cid: 'bafybeifylyzjlrpec75l66kggycx65yuouyavweaaqxmf22jvbtnmmaqru', - }, - }), - ).resolves.toBeUndefined(); - - expect(fakeSdk.storage().delete).toHaveBeenCalledWith({ - cid: 'bafybeifylyzjlrpec75l66kggycx65yuouyavweaaqxmf22jvbtnmmaqru', - }); - expect(output.log).toHaveBeenCalledWith( - 'Processing cid: bafybeifylyzjlrpec75l66kggycx65yuouyavweaaqxmf22jvbtnmmaqru', - ); - expect(output.success).toHaveBeenCalled(); - }); - - it('should delete storage by name', async () => { - const accessTokenService = new PersonalAccessTokenService({ - personalAccessToken: '', - }); - const fakeSdk = new FleekSdk({ accessTokenService }); - - await expect( - deleteStorageAction({ - sdk: fakeSdk, - args: { - name: 'basic.car', - }, - }), - ).resolves.toBeUndefined(); - - expect(fakeSdk.storage().delete).toHaveBeenCalledWith({ - cid: 'bafybeifylyzjlrpec75l66kggycx65yuouyavweaaqxmf22jvbtnmmaqru', - }); - expect(fakeSdk.storage().delete).toHaveBeenCalledWith({ - cid: 'bafkreieasoapp3osmpdt2lwdqy6oqx75nhdsxgkoswyjuwy2675eyhvcg4', - }); - expect(output.log).toHaveBeenCalledWith( - 'Processing cid: bafybeifylyzjlrpec75l66kggycx65yuouyavweaaqxmf22jvbtnmmaqru name: basic.car', - ); - expect(output.log).toHaveBeenCalledWith( - 'Processing cid: bafkreieasoapp3osmpdt2lwdqy6oqx75nhdsxgkoswyjuwy2675eyhvcg4 name: basic.car', - ); - expect(output.success).toHaveBeenCalledTimes(2); - }); - - it('should delete 1 out of 2 storage by name and return 500 on 1', async () => { - const accessTokenService = new PersonalAccessTokenService({ - personalAccessToken: '', - }); - const fakeSdk = new FleekSdk({ accessTokenService }); - - await expect( - deleteStorageAction({ - sdk: fakeSdk, - args: { - name: 'basic1Fail.car', - }, - }), - ).resolves.toBeUndefined(); - - expect(fakeSdk.storage().delete).toHaveBeenCalledWith({ - cid: 'bafkreidlixt2jjjsdxxdzgv3spe46mcwtpsmlxgumpsyaru6n6fn3jz2zy', - }); - expect(fakeSdk.storage().delete).toHaveBeenCalledWith({ - cid: 'bafkreiebwzjtd62ctklmmidldy2z2exinzr2mc72tzhkbe7ftjxm7cwnle', - }); - expect(output.log).toHaveBeenCalledWith( - 'Processing cid: bafkreidlixt2jjjsdxxdzgv3spe46mcwtpsmlxgumpsyaru6n6fn3jz2zy name: basic1Fail.car', - ); - expect(output.log).toHaveBeenCalledWith( - 'Processing cid: bafkreiebwzjtd62ctklmmidldy2z2exinzr2mc72tzhkbe7ftjxm7cwnle name: basic1Fail.car', - ); - expect(output.success).toHaveBeenCalledTimes(1); - expect(output.error).toHaveBeenCalledTimes(1); - }); - - it('should delete all storage by name using batch processing', async () => { - const accessTokenService = new PersonalAccessTokenService({ - personalAccessToken: '', - }); - const fakeSdk = new FleekSdk({ accessTokenService }); - - await expect( - deleteStorageAction({ - sdk: fakeSdk, - args: { - name: 'basicBatch.car', - }, - }), - ).resolves.toBeUndefined(); - - expect(fakeSdk.storage().delete).toHaveBeenCalledTimes(25); - expect(output.log).toHaveBeenCalledTimes(25); - expect(output.success).toHaveBeenCalledTimes(25); - }); - - it('should not process the full batch and just return error on 401', async () => { - const accessTokenService = new PersonalAccessTokenService({ - personalAccessToken: '', - }); - const fakeSdk = new FleekSdk({ accessTokenService }); - - await expect( - deleteStorageAction({ - sdk: fakeSdk, - args: { - name: 'basicBatchUnauthorized.car', - }, - }), - ).resolves.toBeUndefined(); - - expect(fakeSdk.storage().delete).toHaveBeenCalledTimes(10); - expect(output.log).toHaveBeenCalledTimes(10); - expect(output.error).toHaveBeenCalledTimes(1); - }); -}); diff --git a/src/commands/storage/delete.ts b/src/commands/storage/delete.ts deleted file mode 100644 index 76b0f8eb..00000000 --- a/src/commands/storage/delete.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { InvalidCidError } from '@fleek-platform/errors'; -import { CID } from 'multiformats'; - -import { output } from '../../cli'; -import type { SdkGuardedFunction } from '../../guards/types'; -import { withGuards } from '../../guards/withGuards'; -import { processPromisesBatch } from '../../utils/processPromisesBatch'; -import { t } from '../../utils/translation'; - -type DeleteActionArgs = { - cid?: string; - name?: string; -}; - -export const deleteStorageAction: SdkGuardedFunction< - DeleteActionArgs -> = async ({ sdk, args }) => { - const { cid, name } = args; - - const cidsToDelete = []; - - if (typeof name === 'string') { - const splitFilename = name.split('.'); - const extension = (splitFilename.length > 1 && splitFilename.pop()) || ''; - const filename = splitFilename.join('.'); - const storage = await sdk.storage().getByFilename({ filename, extension }); - for (const s of storage) { - cidsToDelete.push(s.cid); - } - } else if (cid) { - try { - CID.parse(cid); - } catch (err) { - throw new InvalidCidError({ name: cid }); - } - - cidsToDelete.push(cid); - } - - try { - await processPromisesBatch( - cidsToDelete, - async (cid: string): Promise => { - const response = await sdk.storage().delete({ cid }); - output.log( - `${t('processing')}${cid ? ` cid: ${cid}` : ''}${name ? ` name: ${name}` : ''}`, - ); - - if (response.status === 200) { - output.success( - t('commonItemActionSuccess', { - subject: cid ? `CID ${cid}` : `filename ${name}`, - action: t('deleted'), - }), - ); - } else if (response.status === 500) { - // 500 status should be caught and should not affect other deletes in case of multiple deletes - output.error( - t('commonItemActionFailure', { - action: t('delete'), - subject: `${t('storage')} "`, - message: `${response.body.message}`, - }), - ); - } else { - // eslint-disable-next-line fleek-custom/no-default-error - throw new Error(response.body.message); - } - - return; - }, - ); - } catch (error) { - if (error instanceof Error) { - output.error(error.message); - } else { - console.error(error); - } - } -}; - -export const deleteStorageActionHandler = withGuards(deleteStorageAction, { - scopes: { - authenticated: true, - project: true, - site: false, - }, -}); diff --git a/src/commands/storage/index.ts b/src/commands/storage/index.ts index 2023087f..399db711 100644 --- a/src/commands/storage/index.ts +++ b/src/commands/storage/index.ts @@ -2,8 +2,6 @@ import type { Command } from 'commander'; import { output } from '../../cli'; import { t } from '../../utils/translation'; -import { addStorageActionHandler } from './add'; -import { deleteStorageActionHandler } from './delete'; import { getStorageActionHandler } from './get'; import { listStorageActionHandler } from './list'; @@ -44,38 +42,5 @@ export default (program: Command): Command => { return getStorageActionHandler({ cid: options.cid, name: options.name }); }); - const deleteStorage = cmd - .command('delete') - .description(t('storageDescription', { action: t('delete') })) - .option('-c, --cid ', t('storageCidOption', { action: t('delete') })) - .option( - '-n, --name ', - t('storageNameOption', { action: t('delete') }), - ); - - deleteStorage.action((options: { cid?: string; name?: string }) => { - if ((!options.name && !options.cid) || (options.name && options.cid)) { - if ( - !getStorage.args.includes('help') && - !getStorage.optsWithGlobals().help - ) { - output.error(t('storageMissingOptCidOrName')); - } - - output.printNewLine(); - deleteStorage.outputHelp(); - - return; - } - - return deleteStorageActionHandler({ cid: options.cid, name: options.name }); - }); - - cmd - .command('add') - .description(t('storageAddDescription')) - .argument('', t('ipfsAddPathDescription')) - .action((path: string) => addStorageActionHandler({ path })); - return cmd; };