From 2ee4a4a64deb45537d8440f2a83218e6faea1e3b Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Mon, 4 Dec 2023 12:31:39 +0100 Subject: [PATCH 01/15] Update storage bucket API endpoints --- packages/sdk/README.md | 2 +- packages/sdk/src/modules/storage/directory.ts | 4 ++-- packages/sdk/src/modules/storage/file.ts | 17 ++++++++++++----- .../sdk/src/modules/storage/storage-bucket.ts | 6 +++--- packages/sdk/src/tests/storage.test.ts | 3 ++- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index df9738b..5ee250f 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -151,7 +151,7 @@ await bucket.listObjects({ }); await bucket.listFiles({ fileStatus: FileStatus.UPLOADED }); const file = await bucket.file('2195521d-15cc-4f6e-abf2-13866f9c6e03').get(); -await bucket.deleteFile('2195521d-15cc-4f6e-abf2-13866f9c6e03'); +await bucket.file('2195521d-15cc-4f6e-abf2-13866f9c6e03').delete(); ``` ### Detailed Storage docs diff --git a/packages/sdk/src/modules/storage/directory.ts b/packages/sdk/src/modules/storage/directory.ts index db77f07..40df290 100644 --- a/packages/sdk/src/modules/storage/directory.ts +++ b/packages/sdk/src/modules/storage/directory.ts @@ -42,7 +42,7 @@ export class Directory extends ApillonModel { public content: (File | Directory)[] = []; /** - * Constructor which should only be called via HostingWebsite class. + * Constructor which should only be called via StorageBucket class. * @param bucketUuid Unique identifier of the directory's bucket. * @param directoryUuid Unique identifier of the directory. * @param data Data to populate the directory with. @@ -54,7 +54,7 @@ export class Directory extends ApillonModel { ) { super(directoryUuid); this.bucketUuid = bucketUuid; - this.API_PREFIX = `/storage/${bucketUuid}`; + this.API_PREFIX = `/storage/buckets/${bucketUuid}`; this.populate(data); } diff --git a/packages/sdk/src/modules/storage/file.ts b/packages/sdk/src/modules/storage/file.ts index 5ca1d82..6afa860 100644 --- a/packages/sdk/src/modules/storage/file.ts +++ b/packages/sdk/src/modules/storage/file.ts @@ -1,7 +1,7 @@ -import { AxiosResponse } from 'axios'; import { ApillonModel } from '../../lib/apillon'; import { ApillonApi } from '../../lib/apillon-api'; import { FileStatus, StorageContentType } from '../../types/storage'; +import { IApillonResponse } from '../../docs-index'; export class File extends ApillonModel { /** @@ -50,7 +50,7 @@ export class File extends ApillonModel { public path: string = null; /** - * Constructor which should only be called via HostingWebsite class. + * Constructor which should only be called via StorageBucket class. * @param bucketUuid Unique identifier of the file's bucket. * @param directoryUuid Unique identifier of the file's directory. * @param fileUuid Unique identifier of the file. @@ -65,7 +65,7 @@ export class File extends ApillonModel { super(fileUuid); this.bucketUuid = bucketUuid; this.directoryUuid = directoryUuid; - this.API_PREFIX = `/storage/${bucketUuid}/file/${fileUuid}`; + this.API_PREFIX = `/storage/buckets/${bucketUuid}/files/${fileUuid}`; this.status = data?.fileStatus; this.populate(data); } @@ -75,12 +75,19 @@ export class File extends ApillonModel { */ async get(): Promise { const { data } = await ApillonApi.get< - AxiosResponse - >(`${this.API_PREFIX}/detail`); + IApillonResponse + >(this.API_PREFIX); this.status = data.fileStatus; return this.populate(data); } + /** + * Deletes a file from the bucket. + */ + async delete(): Promise { + await ApillonApi.delete(this.API_PREFIX); + } + protected override serializeFilter(key: string, value: any) { const serialized = super.serializeFilter(key, value); const enums = { diff --git a/packages/sdk/src/modules/storage/storage-bucket.ts b/packages/sdk/src/modules/storage/storage-bucket.ts index ec5f9bb..55f7ebc 100644 --- a/packages/sdk/src/modules/storage/storage-bucket.ts +++ b/packages/sdk/src/modules/storage/storage-bucket.ts @@ -41,7 +41,7 @@ export class StorageBucket extends ApillonModel { */ constructor(uuid: string, data?: Partial) { super(uuid); - this.API_PREFIX = `/storage/${uuid}`; + this.API_PREFIX = `/storage/buckets/${uuid}`; this.populate(data); } @@ -123,11 +123,11 @@ export class StorageBucket extends ApillonModel { /** * Gets file instance. - * @param fileUuid Uuid of the file. + * @param fileUuid UUID of the file. * @returns Instance of file. */ file(fileUuid: string): File { - return new File(this.uuid, null, fileUuid, {}); + return new File(this.uuid, null, fileUuid); } /** diff --git a/packages/sdk/src/tests/storage.test.ts b/packages/sdk/src/tests/storage.test.ts index dd62a00..f0a61ab 100644 --- a/packages/sdk/src/tests/storage.test.ts +++ b/packages/sdk/src/tests/storage.test.ts @@ -128,6 +128,7 @@ describe('Storage tests', () => { const storage = new Storage(config); await storage .bucket(bucketUUID) - .deleteFile('cf6a0d3d-2abd-4a0d-85c1-10b8f04cd4fc'); + .file('eddc52cf-92d2-436e-b6de-52d7cad621c2') + .delete(); }); }); From f1890631898984316440a34e3ff0020b2952d3df Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Mon, 4 Dec 2023 14:36:15 +0100 Subject: [PATCH 02/15] Add delete directory method --- packages/sdk/src/modules/storage/directory.ts | 9 ++++++++- packages/sdk/src/modules/storage/storage-bucket.ts | 11 ++++++----- packages/sdk/src/tests/storage.test.ts | 8 ++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/sdk/src/modules/storage/directory.ts b/packages/sdk/src/modules/storage/directory.ts index 40df290..495f598 100644 --- a/packages/sdk/src/modules/storage/directory.ts +++ b/packages/sdk/src/modules/storage/directory.ts @@ -54,7 +54,7 @@ export class Directory extends ApillonModel { ) { super(directoryUuid); this.bucketUuid = bucketUuid; - this.API_PREFIX = `/storage/buckets/${bucketUuid}`; + this.API_PREFIX = `/storage/buckets/${bucketUuid}/directories/${directoryUuid}`; this.populate(data); } @@ -90,6 +90,13 @@ export class Directory extends ApillonModel { return this.content; } + /** + * Deletes a directory from the bucket. + */ + async delete(): Promise { + await ApillonApi.delete(this.API_PREFIX); + } + protected serializeFilter(key: string, value: string) { const serialized = super.serializeFilter(key, value); const enums = { diff --git a/packages/sdk/src/modules/storage/storage-bucket.ts b/packages/sdk/src/modules/storage/storage-bucket.ts index 55f7ebc..6eacdcf 100644 --- a/packages/sdk/src/modules/storage/storage-bucket.ts +++ b/packages/sdk/src/modules/storage/storage-bucket.ts @@ -124,17 +124,18 @@ export class StorageBucket extends ApillonModel { /** * Gets file instance. * @param fileUuid UUID of the file. - * @returns Instance of file. + * @returns File instance. */ file(fileUuid: string): File { return new File(this.uuid, null, fileUuid); } /** - * Deletes a file from the bucket. - * @param fileUuid Uuid of the file. + * Gets a directory instance. + * @param directoryUuid UUID of the directory. + * @returns Directory instance. */ - async deleteFile(fileUuid: string): Promise { - await ApillonApi.delete(`/storage/buckets/${this.uuid}/files/${fileUuid}`); + directory(directoryUuid: string): Directory { + return new Directory(this.uuid, directoryUuid); } } diff --git a/packages/sdk/src/tests/storage.test.ts b/packages/sdk/src/tests/storage.test.ts index f0a61ab..546fb5c 100644 --- a/packages/sdk/src/tests/storage.test.ts +++ b/packages/sdk/src/tests/storage.test.ts @@ -131,4 +131,12 @@ describe('Storage tests', () => { .file('eddc52cf-92d2-436e-b6de-52d7cad621c2') .delete(); }); + + test.skip('delete a directory', async () => { + const storage = new Storage(config); + await storage + .bucket(bucketUUID) + .directory('eddc52cf-92d2-436e-b6de-52d7cad621c2') + .delete(); + }); }); From cc483bd2633c3cbebc0bc456a06c0b3de0018f67 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Mon, 4 Dec 2023 15:02:31 +0100 Subject: [PATCH 03/15] Update README.md --- packages/sdk/README.md | 161 +++++++++++++++-------- packages/sdk/src/modules/storage/file.ts | 2 +- packages/sdk/src/tests/storage.test.ts | 2 +- 3 files changed, 107 insertions(+), 58 deletions(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 5ee250f..9209ad0 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -19,8 +19,6 @@ To be able to use Apillon SDK, you must register an account at [Apillon.io](http SDK package is available on [NPM](https://www.npmjs.com/package/@apillon/sdk) and you can also check it out directly on [GitHub](https://github.com/Apillon/sdk). -For additional information on using the SDK, see the [Apillon documentation](https://wiki.apillon.io). - ### Installation ```sh @@ -44,6 +42,9 @@ Alternatively, you can populate the `APILLON_API_KEY` and `APILLON_API_SECRET` e View each individual module examples in the sections below. +### Detailed docs + +This wiki only contains the basic installation and examples of SDK usage. For additional information on using the SDK, see the [Detailed SDK documentation](https://sdk-docs.apillon.io/). ## Hosting @@ -59,6 +60,8 @@ The flow of deploying a new website looks like this: You can also directly deploy uploaded files to production. +For detailed hosting SDK method, class and property documentation visit [SDK hosting docs](https://sdk-docs.apillon.io/classes/Hosting.html). + ### Usage example ```ts @@ -67,101 +70,128 @@ import { DeploymentStatus, Hosting, LogLevel, -} from '@apillon/sdk'; -import * as fs from 'fs'; +} from "@apillon/sdk"; +import * as fs from "fs"; const hosting = new Hosting({ - key: 'yourApiKey', - secret: 'yourApiSecret', - logLevel: LogLevel.VERBOSE, + key: "yourApiKey", + secret: "yourApiSecret", + logLevel: LogLevel.NONE, }); -await hosting.listWebsites({ orderBy: 'createTime' }); -const webpage1 = hosting.website('uuid'); + +// list all websites +await hosting.listWebsites({ orderBy: "createTime" }); + +// create an instance of a website via uuid +const webpage1 = hosting.website("uuid"); + +// gets website information await webpage1.get(); // Upload files from local folder -await webpage1.uploadFromFolder('./my-foler/files/'); +await webpage1.uploadFromFolder("./my-foler/files/"); // Or alternatively, send file buffers as upload parameters -const htmlBuffer = fs.readFileSync('./public/index.html'); +const htmlBuffer = fs.readFileSync("./public/index.html"); await webpage1.uploadFiles( [ { - fileName: 'index.html', - contentType: 'text/html', + fileName: "index.html", + contentType: "text/html", path: null, content: htmlBuffer, }, ], // Upload the files in a new subdirectory in the bucket instead of in the root of the bucket - { wrapWithDirectory: true, directoryPath: 'main/subdir' }, + { wrapWithDirectory: true, directoryPath: "main/subdir" } ); +// deploys uploaded files to staging environment await webpage1.deploy(DeployToEnvironment.TO_STAGING); + +// lists all deployments of a website await webpage1.listDeployments(); + +// gets a specific deployment const deployment = await webpage1 - .deployment('3e0c66ea-317d-4e1f-bcd9-38026c3ea1ee') + .deployment("3e0c66ea-317d-4e1f-bcd9-38026c3ea1ee") .get(); + +// checks if deployment was successful if (deployment.deploymentStatus === DeploymentStatus.SUCCESSFUL) { // done } ``` -### Detailed Hosting docs - -Detailed hosting SDK method, class and property documentation is available [here](https://sdk-docs.apillon.io/classes/Hosting.html). - ## Storage Storage module encapsulates functionalities for Storage service available on Apillon dashboard. +For detailed storage SDK method, class and property documentation visit [SDK storage docs](https://sdk-docs.apillon.io/classes/Storage.html). + ### Usage example ```ts -import { Storage, LogLevel, FileStatus } from '@apillon/sdk'; -import * as fs from 'fs'; +import { Storage, LogLevel, FileStatus } from "@apillon/sdk"; +import * as fs from "fs"; const storage = new Storage({ - key: 'yourApiKey', - secret: 'yourApiSecret', - logLevel: LogLevel.VERBOSE, + key: "yourApiKey", + secret: "yourApiSecret", + logLevel: LogLevel.NONE, }); + +// list buckets await storage.listBuckets({ limit: 5 }); -const bucket = storage.bucket('uuid'); + +// create and instance of a bucket directly through uuid +const bucket = storage.bucket("uuid"); // Upload files from local folder -await bucket.uploadFromFolder('./my-foler/files/'); +await bucket.uploadFromFolder("./my-foler/files/"); // Or alternatively, send file buffers as upload parameters -const htmlBuffer = fs.readFileSync('./public/index.html'); +const htmlBuffer = fs.readFileSync("./public/index.html"); await bucket.uploadFiles( [ { - fileName: 'index.html', - contentType: 'text/html', + fileName: "index.html", + contentType: "text/html", path: null, content: htmlBuffer, }, ], // Upload the files in a new subdirectory in the bucket instead of in the root of the bucket - { wrapWithDirectory: true, directoryPath: 'main/subdir' }, + { wrapWithDirectory: true, directoryPath: "main/subdir" } ); + +// list objects (files, folders) in a bucket await bucket.listObjects({ - directoryUuid: 'eaff2672-3012-46fb-9278-5efacc6cb616', + directoryUuid: "eaff2672-3012-46fb-9278-5efacc6cb616", markedForDeletion: false, limit: 5, }); + +// list all files in a bucket no matter if they are in a folder or not await bucket.listFiles({ fileStatus: FileStatus.UPLOADED }); -const file = await bucket.file('2195521d-15cc-4f6e-abf2-13866f9c6e03').get(); -await bucket.file('2195521d-15cc-4f6e-abf2-13866f9c6e03').delete(); -``` -### Detailed Storage docs +// gets a specific file in a bucket directly through uuid +const file = await bucket.file("2195521d-15cc-4f6e-abf2-13866f9c6e03").get(); -Detailed Storage SDK method, class and property documentation is available [here](https://sdk-docs.apillon.io/classes/Storage.html). +// deletes a file via uuid +await bucket.file("2195521d-15cc-4f6e-abf2-13866f9c6e03").delete(); +// deletes a directory via uuid +await bucket.directory("eddc52cf-92d2-436e-b6de-42d7cad621c3").delete(); +``` ## NFTs NFT module encapsulates functionalities for NFT service available on Apillon dashboard. +For detailed NFT SDK method, class and property documentation visit [SDK NFT docs](https://sdk-docs.apillon.io/classes/Nft.html). + +::: warning +When you transfer ownership of the collection to another account Apillon will lose the ability to perform actions in your name (mint, burn, etc.). Before you transfer ownership make sure you do not need those functionalities via Apillon anymore. +::: + ### Usage example ```ts @@ -171,44 +201,63 @@ import { LogLevel, Nft, TransactionStatus, -} from '@apillon/sdk'; +} from "@apillon/sdk"; const nft = new Nft({ - key: 'yourApiKey', - secret: 'yourApiSecret', - logLevel: LogLevel.VERBOSE, + key: "yourApiKey", + secret: "yourApiSecret", + logLevel: LogLevel.NONE, }); -await nft.create({ + +// create a new collection +const collection1 = await nft.create({ collectionType: CollectionType.GENERIC, chain: EvmChain.MOONBEAM, - name: 'SpaceExplorers', - symbol: 'SE', - description: 'A collection of unique space exploration NFTs.', - baseUri: 'https://moonbeamnfts.com/collections/spaceexplorers/', - baseExtension: 'json', + name: "SpaceExplorers", + symbol: "SE", + description: "A collection of unique space exploration NFTs.", + baseUri: "https://moonbeamnfts.com/collections/spaceexplorers/", + baseExtension: "json", maxSupply: 1000, isRevokable: false, isSoulbound: false, - royaltiesAddress: '0x1234567890abcdef', + royaltiesAddress: "0x1234567890abcdef", royaltiesFees: 5, drop: true, dropStart: 1679875200, dropPrice: 0.05, dropReserve: 100, }); -await nft.listCollections({ search: 'My NFT' }); -const collection = await nft.collection('uuid').get(); -await collection.mint('0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD', 1); + +// check if collection is deployed - available on chain +if (collection1.collectionStatus == CollectionStatus.DEPLOYED) { + console.log("Collection deployed: ", collection1.transactionHash); +} + +// search through collections +await nft.listCollections({ search: "My NFT" }); + +// create and instance of collection directly through uuid +const collection = await nft.collection("uuid").get(); + +// mint a new nft in the collection +await collection.mint("0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", 1); + +// nest mint a new nft if collection type is NESTABLE await collection.nestMint(collection.uuid, 1, 1); -await collection.burn('1'); + +// burn/destroy a specific NFT by its ID if collection is set as revokable +await collection.burn("1"); + +// list confirmed transactions on a collection await collection.listTransactions({ transactionStatus: TransactionStatus.CONFIRMED, }); + +// transfer ownership of a collection away from apillon platform to an address +// NOTE that this will disable the ability to mint/burn etc. from the SDK/API since only the owner +// has this ability await collection.transferOwnership( - '0x5BA8B0c24bA5307b67E619ad500a635204F73bF1', + "0x5BA8B0c24bA5307b67E619ad500a635204F73bF1" ); ``` - -### Detailed NFT docs - -Detailed NFT SDK method, class and property documentation is available [here](https://sdk-docs.apillon.io/classes/Nft.html). \ No newline at end of file diff --git a/packages/sdk/src/modules/storage/file.ts b/packages/sdk/src/modules/storage/file.ts index 6afa860..a5a99e3 100644 --- a/packages/sdk/src/modules/storage/file.ts +++ b/packages/sdk/src/modules/storage/file.ts @@ -1,7 +1,7 @@ import { ApillonModel } from '../../lib/apillon'; import { ApillonApi } from '../../lib/apillon-api'; +import { IApillonResponse } from '../../types/apillon'; import { FileStatus, StorageContentType } from '../../types/storage'; -import { IApillonResponse } from '../../docs-index'; export class File extends ApillonModel { /** diff --git a/packages/sdk/src/tests/storage.test.ts b/packages/sdk/src/tests/storage.test.ts index 546fb5c..5404079 100644 --- a/packages/sdk/src/tests/storage.test.ts +++ b/packages/sdk/src/tests/storage.test.ts @@ -128,7 +128,7 @@ describe('Storage tests', () => { const storage = new Storage(config); await storage .bucket(bucketUUID) - .file('eddc52cf-92d2-436e-b6de-52d7cad621c2') + .file('cf6a0d3d-2abd-4a0d-85c1-10b8f04cd4fc') .delete(); }); From 789893b18198af716915f7b3c633fc5db1aef44f Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Mon, 4 Dec 2023 12:31:39 +0100 Subject: [PATCH 04/15] Update storage bucket API endpoints --- packages/sdk/src/modules/storage/storage-bucket.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/sdk/src/modules/storage/storage-bucket.ts b/packages/sdk/src/modules/storage/storage-bucket.ts index 6eacdcf..71c4d83 100644 --- a/packages/sdk/src/modules/storage/storage-bucket.ts +++ b/packages/sdk/src/modules/storage/storage-bucket.ts @@ -8,7 +8,11 @@ import { } from '../../types/storage'; import { File } from './file'; import { constructUrlWithQueryParams } from '../../lib/common'; -import { IApillonList, IApillonListResponse } from '../../types/apillon'; +import { + IApillonList, + IApillonListResponse, + IApillonResponse, +} from '../../types/apillon'; import { ApillonApi } from '../../lib/apillon-api'; import { uploadFiles } from '../../util/file-utils'; import { ApillonModel } from '../../lib/apillon'; From b86f1ee39cf3a7824bd8cf4eae3f19fc1d7ddea0 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Mon, 4 Dec 2023 15:08:53 +0100 Subject: [PATCH 05/15] Implement IPNS class and SDK methods --- .../src/modules/hosting/hosting-website.ts | 9 +- packages/sdk/src/modules/storage/ipns.ts | 82 +++++++++++++++++++ .../sdk/src/modules/storage/storage-bucket.ts | 47 +++++++++++ packages/sdk/src/modules/storage/storage.ts | 4 +- packages/sdk/src/tests/ipns.test.ts | 67 +++++++++++++++ packages/sdk/src/types/storage.ts | 21 +++++ packages/sdk/src/util/file-utils.ts | 2 +- 7 files changed, 226 insertions(+), 6 deletions(-) create mode 100644 packages/sdk/src/modules/storage/ipns.ts create mode 100644 packages/sdk/src/tests/ipns.test.ts diff --git a/packages/sdk/src/modules/hosting/hosting-website.ts b/packages/sdk/src/modules/hosting/hosting-website.ts index 6e09fa7..86ea268 100644 --- a/packages/sdk/src/modules/hosting/hosting-website.ts +++ b/packages/sdk/src/modules/hosting/hosting-website.ts @@ -94,7 +94,7 @@ export class HostingWebsite extends ApillonModel { * @param {DeployToEnvironment} toEnvironment The environment to deploy to * @returns The new deployment instance */ - public async deploy(toEnvironment: DeployToEnvironment): Promise { + public async deploy(toEnvironment: DeployToEnvironment) { ApillonLogger.log( `Deploying website ${this.uuid} to IPFS (${ toEnvironment === DeployToEnvironment.TO_STAGING @@ -104,9 +104,10 @@ export class HostingWebsite extends ApillonModel { ); ApillonLogger.logWithTime('Initiating deployment'); - const { data } = await ApillonApi.post(`${this.API_PREFIX}/deploy`, { - environment: toEnvironment, - }); + const { data } = await ApillonApi.post>( + `${this.API_PREFIX}/deploy`, + { environment: toEnvironment }, + ); ApillonLogger.logWithTime('Deployment in progress'); diff --git a/packages/sdk/src/modules/storage/ipns.ts b/packages/sdk/src/modules/storage/ipns.ts new file mode 100644 index 0000000..b5922cd --- /dev/null +++ b/packages/sdk/src/modules/storage/ipns.ts @@ -0,0 +1,82 @@ +import { ApillonModel } from '../../lib/apillon'; +import { ApillonApi } from '../../lib/apillon-api'; +import { IApillonResponse } from '../../types/apillon'; + +export class Ipns extends ApillonModel { + /** + * Informational IPNS name, which is set by user to easily organize their IPNS records. + */ + public name: string = null; + + /** + * IPNS record description. + */ + public description: string = null; + + /** + * IPNS name that is used to access IPNS content on IPFS gateway. + */ + public ipnsName: string = null; + + /** + * IPFS value (CID), to which this IPNS points. + */ + public ipnsValue: string = null; + + /** + * IPNS link to Apillon IPFS gateway, where it is possible to see content to which this IPNS points. + */ + public link: string = null; + + /** + * Unique identifier of the IPNS record's bucket. + */ + public bucketUuid: string = null; + + /** + * Constructor which should only be called via StorageBucket class. + * @param bucketUuid Unique identifier of the file's bucket. + * @param ipnsUuid Unique identifier of the IPNS record. + * @param data Data to populate the IPNS record with. + */ + constructor(bucketUuid: string, ipnsUuid: string, data?: Partial) { + super(ipnsUuid); + this.bucketUuid = bucketUuid; + this.API_PREFIX = `/storage/buckets/${bucketUuid}/ipns/${ipnsUuid}`; + this.populate(data); + } + + /** + * Gets IPNS details. + */ + async get(): Promise { + const { data } = await ApillonApi.get>( + this.API_PREFIX, + ); + return this.populate(data); + } + + /** + * Publish an IPNS record to IPFS and link it to a CID. + * @param {string} cid - CID to which this ipns name will point. + * @returns {Promise} + */ + async publish(cid: string): Promise { + const { data } = await ApillonApi.post>( + `${this.API_PREFIX}/publish`, + { cid }, + ); + return this.populate(data); + } + + /** + * Delete an IPNS record from the bucket. + * @returns {Promise} + */ + async delete(): Promise { + const { data } = await ApillonApi.delete>( + this.API_PREFIX, + ); + return this.populate(data); + } +} diff --git a/packages/sdk/src/modules/storage/storage-bucket.ts b/packages/sdk/src/modules/storage/storage-bucket.ts index 71c4d83..de1ead9 100644 --- a/packages/sdk/src/modules/storage/storage-bucket.ts +++ b/packages/sdk/src/modules/storage/storage-bucket.ts @@ -2,7 +2,9 @@ import { Directory } from './directory'; import { FileMetadata, IBucketFilesRequest, + ICreateIpns, IFileUploadRequest, + IPNSListRequest, IStorageBucketContentRequest, StorageContentType, } from '../../types/storage'; @@ -16,6 +18,7 @@ import { import { ApillonApi } from '../../lib/apillon-api'; import { uploadFiles } from '../../util/file-utils'; import { ApillonModel } from '../../lib/apillon'; +import { Ipns } from './ipns'; export class StorageBucket extends ApillonModel { /** @@ -142,4 +145,48 @@ export class StorageBucket extends ApillonModel { directory(directoryUuid: string): Directory { return new Directory(this.uuid, directoryUuid); } + + //#region IPNS methods + + /** + * Gets an IPNS record instance. + * @param ipnsUuid UUID of the IPNS record. + * @returns Ipns instance. + */ + ipns(ipnsUuid: string): Ipns { + return new Ipns(this.uuid, ipnsUuid); + } + + /** + * List all IPNS records for this bucket + * @param {IPNSListRequest?} [params] - Listing query filters + */ + async listIpnsNames(params?: IPNSListRequest) { + const url = constructUrlWithQueryParams( + `/storage/buckets/${this.uuid}/ipns`, + params, + ); + const { data } = await ApillonApi.get< + IApillonListResponse + >(url); + + return { + ...data, + items: data.items.map((ipns) => new Ipns(this.uuid, ipns.ipnsUuid, ipns)), + }; + } + + /** + * Create a new IPNS record for this bucket + * @param {ICreateIpns} body + * @returns {Promise} + */ + async createIpns(body: ICreateIpns): Promise { + const url = `/storage/buckets/${this.uuid}/ipns`; + const { data } = await ApillonApi.post< + IApillonResponse + >(url, body); + return new Ipns(this.uuid, data.ipnsUuid, data); + } + //#endregion } diff --git a/packages/sdk/src/modules/storage/storage.ts b/packages/sdk/src/modules/storage/storage.ts index ac1e94e..49d1b25 100644 --- a/packages/sdk/src/modules/storage/storage.ts +++ b/packages/sdk/src/modules/storage/storage.ts @@ -24,7 +24,9 @@ export class Storage extends ApillonModule { ): Promise> { const url = constructUrlWithQueryParams(this.API_PREFIX, params); - const { data } = await ApillonApi.get>(url); + const { data } = await ApillonApi.get< + IApillonListResponse + >(url); return { ...data, diff --git a/packages/sdk/src/tests/ipns.test.ts b/packages/sdk/src/tests/ipns.test.ts new file mode 100644 index 0000000..e468b78 --- /dev/null +++ b/packages/sdk/src/tests/ipns.test.ts @@ -0,0 +1,67 @@ +import { ApillonConfig } from '../lib/apillon'; +import { Ipns } from '../modules/storage/ipns'; +import { Storage } from '../modules/storage/storage'; +import { getBucketUUID, getConfig } from './helpers/helper'; + +describe('IPNS tests for StorageBucket', () => { + let config: ApillonConfig; + let bucketUUID: string; + let newIpnsUuid: string; + + beforeAll(async () => { + config = getConfig(); + bucketUUID = getBucketUUID(); + }); + + test('List IPNS records in a bucket', async () => { + const storage = new Storage(config); + const response = await storage.bucket(bucketUUID).listIpnsNames(); + expect(response.items).toBeInstanceOf(Array); + expect(response.items.length).toBeGreaterThanOrEqual(0); + }); + + test('Create a new IPNS record', async () => { + const storage = new Storage(config); + const name = 'Test IPNS'; + const description = 'This is a test description'; + const cid = 'QmUxtfFfWFguxSWUUy2FiBsGuH6Px4KYFxJqNYJRiDpemj'; + const ipns = await storage.bucket(bucketUUID).createIpns({ + name, + description, + cid, + }); + expect(ipns).toBeDefined(); + expect(ipns.name).toEqual(name); + expect(ipns.description).toEqual(description); + expect(ipns.ipnsValue).toEqual(`/ipfs/${cid}`); + newIpnsUuid = ipns.uuid; // Save the new IPNS UUID for later use in other tests + }); + + test('Get a specific IPNS record', async () => { + const storage = new Storage(config); + const ipns = await storage.bucket(bucketUUID).ipns(newIpnsUuid).get(); + expect(ipns).toBeDefined(); + expect(ipns.name).toEqual('Test IPNS'); + expect(ipns.uuid).toEqual(newIpnsUuid); + }); + + test('Publish an IPNS record', async () => { + const storage = new Storage(config); + const cid = 'Qmakf2aN7wzt5u9H3RadGjfotu62JsDfBq8hHzGsV2LZFx'; + const ipns = await storage + .bucket(bucketUUID) + .ipns(newIpnsUuid) + .publish(cid); + expect(ipns).toBeDefined(); + expect(ipns.ipnsValue).toEqual(`/ipfs/${cid}`); + expect(ipns.uuid).toEqual(newIpnsUuid); + }); + + test('Delete an IPNS record', async () => { + const storage = new Storage(config); + const ipns = await storage.bucket(bucketUUID).ipns(newIpnsUuid).delete(); + expect(ipns).toBeDefined(); + expect(ipns.name).toEqual('Test IPNS'); + expect(ipns.uuid).toEqual(newIpnsUuid); + }); +}); diff --git a/packages/sdk/src/types/storage.ts b/packages/sdk/src/types/storage.ts index 1b18faa..99336e3 100644 --- a/packages/sdk/src/types/storage.ts +++ b/packages/sdk/src/types/storage.ts @@ -63,3 +63,24 @@ export interface IFileUploadResponse { files: FileMetadata[]; sessionUuid: string; } + +export interface IPNSListRequest extends IApillonPagination { + /** + * IPNS name, that is used to access ipns content on ipfs gateway + */ + ipnsName: string; + /** + * IPFS value (CID), to which this ipns points + */ + ipnsValue: string; +} + +export interface ICreateIpns { + name: string; + description?: string; + /** + * CID to which this IPNS name will point. + * If this property is specified, API executes ipns publish which sets ipnsName and ipnsValue properties + */ + cid?: string; +} diff --git a/packages/sdk/src/util/file-utils.ts b/packages/sdk/src/util/file-utils.ts index 2170989..1bbcf50 100644 --- a/packages/sdk/src/util/file-utils.ts +++ b/packages/sdk/src/util/file-utils.ts @@ -116,7 +116,7 @@ export async function uploadFiles( ApillonLogger.logWithTime('File upload complete.'); ApillonLogger.log('Closing upload session...'); - await ApillonApi.post(`${apiPrefix}/upload/${sessionUuid}/end`, params); + await ApillonApi.post(`${apiPrefix}/upload/${sessionUuid}/end`, params); ApillonLogger.logWithTime('Session ended.'); } From 051b57cff44ec2f9d42dacb01d5377e3f215118e Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Mon, 4 Dec 2023 15:02:31 +0100 Subject: [PATCH 06/15] Update README.md --- packages/sdk/README.md | 161 ++++++++++++------ packages/sdk/src/modules/storage/directory.ts | 2 +- packages/sdk/src/modules/storage/file.ts | 2 +- packages/sdk/src/tests/storage.test.ts | 2 +- 4 files changed, 108 insertions(+), 59 deletions(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 5ee250f..9209ad0 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -19,8 +19,6 @@ To be able to use Apillon SDK, you must register an account at [Apillon.io](http SDK package is available on [NPM](https://www.npmjs.com/package/@apillon/sdk) and you can also check it out directly on [GitHub](https://github.com/Apillon/sdk). -For additional information on using the SDK, see the [Apillon documentation](https://wiki.apillon.io). - ### Installation ```sh @@ -44,6 +42,9 @@ Alternatively, you can populate the `APILLON_API_KEY` and `APILLON_API_SECRET` e View each individual module examples in the sections below. +### Detailed docs + +This wiki only contains the basic installation and examples of SDK usage. For additional information on using the SDK, see the [Detailed SDK documentation](https://sdk-docs.apillon.io/). ## Hosting @@ -59,6 +60,8 @@ The flow of deploying a new website looks like this: You can also directly deploy uploaded files to production. +For detailed hosting SDK method, class and property documentation visit [SDK hosting docs](https://sdk-docs.apillon.io/classes/Hosting.html). + ### Usage example ```ts @@ -67,101 +70,128 @@ import { DeploymentStatus, Hosting, LogLevel, -} from '@apillon/sdk'; -import * as fs from 'fs'; +} from "@apillon/sdk"; +import * as fs from "fs"; const hosting = new Hosting({ - key: 'yourApiKey', - secret: 'yourApiSecret', - logLevel: LogLevel.VERBOSE, + key: "yourApiKey", + secret: "yourApiSecret", + logLevel: LogLevel.NONE, }); -await hosting.listWebsites({ orderBy: 'createTime' }); -const webpage1 = hosting.website('uuid'); + +// list all websites +await hosting.listWebsites({ orderBy: "createTime" }); + +// create an instance of a website via uuid +const webpage1 = hosting.website("uuid"); + +// gets website information await webpage1.get(); // Upload files from local folder -await webpage1.uploadFromFolder('./my-foler/files/'); +await webpage1.uploadFromFolder("./my-foler/files/"); // Or alternatively, send file buffers as upload parameters -const htmlBuffer = fs.readFileSync('./public/index.html'); +const htmlBuffer = fs.readFileSync("./public/index.html"); await webpage1.uploadFiles( [ { - fileName: 'index.html', - contentType: 'text/html', + fileName: "index.html", + contentType: "text/html", path: null, content: htmlBuffer, }, ], // Upload the files in a new subdirectory in the bucket instead of in the root of the bucket - { wrapWithDirectory: true, directoryPath: 'main/subdir' }, + { wrapWithDirectory: true, directoryPath: "main/subdir" } ); +// deploys uploaded files to staging environment await webpage1.deploy(DeployToEnvironment.TO_STAGING); + +// lists all deployments of a website await webpage1.listDeployments(); + +// gets a specific deployment const deployment = await webpage1 - .deployment('3e0c66ea-317d-4e1f-bcd9-38026c3ea1ee') + .deployment("3e0c66ea-317d-4e1f-bcd9-38026c3ea1ee") .get(); + +// checks if deployment was successful if (deployment.deploymentStatus === DeploymentStatus.SUCCESSFUL) { // done } ``` -### Detailed Hosting docs - -Detailed hosting SDK method, class and property documentation is available [here](https://sdk-docs.apillon.io/classes/Hosting.html). - ## Storage Storage module encapsulates functionalities for Storage service available on Apillon dashboard. +For detailed storage SDK method, class and property documentation visit [SDK storage docs](https://sdk-docs.apillon.io/classes/Storage.html). + ### Usage example ```ts -import { Storage, LogLevel, FileStatus } from '@apillon/sdk'; -import * as fs from 'fs'; +import { Storage, LogLevel, FileStatus } from "@apillon/sdk"; +import * as fs from "fs"; const storage = new Storage({ - key: 'yourApiKey', - secret: 'yourApiSecret', - logLevel: LogLevel.VERBOSE, + key: "yourApiKey", + secret: "yourApiSecret", + logLevel: LogLevel.NONE, }); + +// list buckets await storage.listBuckets({ limit: 5 }); -const bucket = storage.bucket('uuid'); + +// create and instance of a bucket directly through uuid +const bucket = storage.bucket("uuid"); // Upload files from local folder -await bucket.uploadFromFolder('./my-foler/files/'); +await bucket.uploadFromFolder("./my-foler/files/"); // Or alternatively, send file buffers as upload parameters -const htmlBuffer = fs.readFileSync('./public/index.html'); +const htmlBuffer = fs.readFileSync("./public/index.html"); await bucket.uploadFiles( [ { - fileName: 'index.html', - contentType: 'text/html', + fileName: "index.html", + contentType: "text/html", path: null, content: htmlBuffer, }, ], // Upload the files in a new subdirectory in the bucket instead of in the root of the bucket - { wrapWithDirectory: true, directoryPath: 'main/subdir' }, + { wrapWithDirectory: true, directoryPath: "main/subdir" } ); + +// list objects (files, folders) in a bucket await bucket.listObjects({ - directoryUuid: 'eaff2672-3012-46fb-9278-5efacc6cb616', + directoryUuid: "eaff2672-3012-46fb-9278-5efacc6cb616", markedForDeletion: false, limit: 5, }); + +// list all files in a bucket no matter if they are in a folder or not await bucket.listFiles({ fileStatus: FileStatus.UPLOADED }); -const file = await bucket.file('2195521d-15cc-4f6e-abf2-13866f9c6e03').get(); -await bucket.file('2195521d-15cc-4f6e-abf2-13866f9c6e03').delete(); -``` -### Detailed Storage docs +// gets a specific file in a bucket directly through uuid +const file = await bucket.file("2195521d-15cc-4f6e-abf2-13866f9c6e03").get(); -Detailed Storage SDK method, class and property documentation is available [here](https://sdk-docs.apillon.io/classes/Storage.html). +// deletes a file via uuid +await bucket.file("2195521d-15cc-4f6e-abf2-13866f9c6e03").delete(); +// deletes a directory via uuid +await bucket.directory("eddc52cf-92d2-436e-b6de-42d7cad621c3").delete(); +``` ## NFTs NFT module encapsulates functionalities for NFT service available on Apillon dashboard. +For detailed NFT SDK method, class and property documentation visit [SDK NFT docs](https://sdk-docs.apillon.io/classes/Nft.html). + +::: warning +When you transfer ownership of the collection to another account Apillon will lose the ability to perform actions in your name (mint, burn, etc.). Before you transfer ownership make sure you do not need those functionalities via Apillon anymore. +::: + ### Usage example ```ts @@ -171,44 +201,63 @@ import { LogLevel, Nft, TransactionStatus, -} from '@apillon/sdk'; +} from "@apillon/sdk"; const nft = new Nft({ - key: 'yourApiKey', - secret: 'yourApiSecret', - logLevel: LogLevel.VERBOSE, + key: "yourApiKey", + secret: "yourApiSecret", + logLevel: LogLevel.NONE, }); -await nft.create({ + +// create a new collection +const collection1 = await nft.create({ collectionType: CollectionType.GENERIC, chain: EvmChain.MOONBEAM, - name: 'SpaceExplorers', - symbol: 'SE', - description: 'A collection of unique space exploration NFTs.', - baseUri: 'https://moonbeamnfts.com/collections/spaceexplorers/', - baseExtension: 'json', + name: "SpaceExplorers", + symbol: "SE", + description: "A collection of unique space exploration NFTs.", + baseUri: "https://moonbeamnfts.com/collections/spaceexplorers/", + baseExtension: "json", maxSupply: 1000, isRevokable: false, isSoulbound: false, - royaltiesAddress: '0x1234567890abcdef', + royaltiesAddress: "0x1234567890abcdef", royaltiesFees: 5, drop: true, dropStart: 1679875200, dropPrice: 0.05, dropReserve: 100, }); -await nft.listCollections({ search: 'My NFT' }); -const collection = await nft.collection('uuid').get(); -await collection.mint('0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD', 1); + +// check if collection is deployed - available on chain +if (collection1.collectionStatus == CollectionStatus.DEPLOYED) { + console.log("Collection deployed: ", collection1.transactionHash); +} + +// search through collections +await nft.listCollections({ search: "My NFT" }); + +// create and instance of collection directly through uuid +const collection = await nft.collection("uuid").get(); + +// mint a new nft in the collection +await collection.mint("0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", 1); + +// nest mint a new nft if collection type is NESTABLE await collection.nestMint(collection.uuid, 1, 1); -await collection.burn('1'); + +// burn/destroy a specific NFT by its ID if collection is set as revokable +await collection.burn("1"); + +// list confirmed transactions on a collection await collection.listTransactions({ transactionStatus: TransactionStatus.CONFIRMED, }); + +// transfer ownership of a collection away from apillon platform to an address +// NOTE that this will disable the ability to mint/burn etc. from the SDK/API since only the owner +// has this ability await collection.transferOwnership( - '0x5BA8B0c24bA5307b67E619ad500a635204F73bF1', + "0x5BA8B0c24bA5307b67E619ad500a635204F73bF1" ); ``` - -### Detailed NFT docs - -Detailed NFT SDK method, class and property documentation is available [here](https://sdk-docs.apillon.io/classes/Nft.html). \ No newline at end of file diff --git a/packages/sdk/src/modules/storage/directory.ts b/packages/sdk/src/modules/storage/directory.ts index 495f598..bff094c 100644 --- a/packages/sdk/src/modules/storage/directory.ts +++ b/packages/sdk/src/modules/storage/directory.ts @@ -67,7 +67,7 @@ export class Directory extends ApillonModel { this.content = []; params.directoryUuid = this.uuid; const url = constructUrlWithQueryParams( - `${this.API_PREFIX}/content`, + `/storage/buckets/${this.bucketUuid}/content`, params, ); const { data } = await ApillonApi.get< diff --git a/packages/sdk/src/modules/storage/file.ts b/packages/sdk/src/modules/storage/file.ts index 6afa860..a5a99e3 100644 --- a/packages/sdk/src/modules/storage/file.ts +++ b/packages/sdk/src/modules/storage/file.ts @@ -1,7 +1,7 @@ import { ApillonModel } from '../../lib/apillon'; import { ApillonApi } from '../../lib/apillon-api'; +import { IApillonResponse } from '../../types/apillon'; import { FileStatus, StorageContentType } from '../../types/storage'; -import { IApillonResponse } from '../../docs-index'; export class File extends ApillonModel { /** diff --git a/packages/sdk/src/tests/storage.test.ts b/packages/sdk/src/tests/storage.test.ts index 546fb5c..5404079 100644 --- a/packages/sdk/src/tests/storage.test.ts +++ b/packages/sdk/src/tests/storage.test.ts @@ -128,7 +128,7 @@ describe('Storage tests', () => { const storage = new Storage(config); await storage .bucket(bucketUUID) - .file('eddc52cf-92d2-436e-b6de-52d7cad621c2') + .file('cf6a0d3d-2abd-4a0d-85c1-10b8f04cd4fc') .delete(); }); From 359492fd3fcbbfd193366d9211680addf848dee3 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Tue, 5 Dec 2023 12:33:59 +0100 Subject: [PATCH 07/15] Fix directory API prefix --- packages/sdk/src/modules/storage/directory.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/sdk/src/modules/storage/directory.ts b/packages/sdk/src/modules/storage/directory.ts index bff094c..a6b26b4 100644 --- a/packages/sdk/src/modules/storage/directory.ts +++ b/packages/sdk/src/modules/storage/directory.ts @@ -54,7 +54,7 @@ export class Directory extends ApillonModel { ) { super(directoryUuid); this.bucketUuid = bucketUuid; - this.API_PREFIX = `/storage/buckets/${bucketUuid}/directories/${directoryUuid}`; + this.API_PREFIX = `/storage/buckets/${bucketUuid}`; this.populate(data); } @@ -67,7 +67,7 @@ export class Directory extends ApillonModel { this.content = []; params.directoryUuid = this.uuid; const url = constructUrlWithQueryParams( - `/storage/buckets/${this.bucketUuid}/content`, + `${this.API_PREFIX}/content`, params, ); const { data } = await ApillonApi.get< @@ -94,7 +94,7 @@ export class Directory extends ApillonModel { * Deletes a directory from the bucket. */ async delete(): Promise { - await ApillonApi.delete(this.API_PREFIX); + await ApillonApi.delete(`${this.API_PREFIX}/directories/${this.uuid}`); } protected serializeFilter(key: string, value: string) { From d70caf2a6aab0248e5b2e79692339f964d67f82b Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Tue, 5 Dec 2023 13:29:16 +0100 Subject: [PATCH 08/15] Add logging after deletion --- packages/sdk/src/modules/storage/directory.ts | 2 ++ packages/sdk/src/modules/storage/file.ts | 2 ++ packages/sdk/src/modules/storage/ipns.ts | 3 +++ packages/sdk/src/types/storage.ts | 4 ++-- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/modules/storage/directory.ts b/packages/sdk/src/modules/storage/directory.ts index a6b26b4..fbcb6b2 100644 --- a/packages/sdk/src/modules/storage/directory.ts +++ b/packages/sdk/src/modules/storage/directory.ts @@ -7,6 +7,7 @@ import { constructUrlWithQueryParams } from '../../lib/common'; import { ApillonApi } from '../../lib/apillon-api'; import { IApillonListResponse } from '../../types/apillon'; import { ApillonModel } from '../../lib/apillon'; +import { ApillonLogger } from '../../lib/apillon-logger'; export class Directory extends ApillonModel { /** @@ -95,6 +96,7 @@ export class Directory extends ApillonModel { */ async delete(): Promise { await ApillonApi.delete(`${this.API_PREFIX}/directories/${this.uuid}`); + ApillonLogger.log('Directory deleted successfully'); } protected serializeFilter(key: string, value: string) { diff --git a/packages/sdk/src/modules/storage/file.ts b/packages/sdk/src/modules/storage/file.ts index a5a99e3..c25731d 100644 --- a/packages/sdk/src/modules/storage/file.ts +++ b/packages/sdk/src/modules/storage/file.ts @@ -1,5 +1,6 @@ import { ApillonModel } from '../../lib/apillon'; import { ApillonApi } from '../../lib/apillon-api'; +import { ApillonLogger } from '../../lib/apillon-logger'; import { IApillonResponse } from '../../types/apillon'; import { FileStatus, StorageContentType } from '../../types/storage'; @@ -86,6 +87,7 @@ export class File extends ApillonModel { */ async delete(): Promise { await ApillonApi.delete(this.API_PREFIX); + ApillonLogger.log('File deleted successfully'); } protected override serializeFilter(key: string, value: any) { diff --git a/packages/sdk/src/modules/storage/ipns.ts b/packages/sdk/src/modules/storage/ipns.ts index b5922cd..08dc86c 100644 --- a/packages/sdk/src/modules/storage/ipns.ts +++ b/packages/sdk/src/modules/storage/ipns.ts @@ -1,5 +1,6 @@ import { ApillonModel } from '../../lib/apillon'; import { ApillonApi } from '../../lib/apillon-api'; +import { ApillonLogger } from '../../lib/apillon-logger'; import { IApillonResponse } from '../../types/apillon'; export class Ipns extends ApillonModel { @@ -66,6 +67,7 @@ export class Ipns extends ApillonModel { `${this.API_PREFIX}/publish`, { cid }, ); + ApillonLogger.log('IPNS record published successfully'); return this.populate(data); } @@ -77,6 +79,7 @@ export class Ipns extends ApillonModel { const { data } = await ApillonApi.delete>( this.API_PREFIX, ); + ApillonLogger.log('IPNS record deleted successfully'); return this.populate(data); } } diff --git a/packages/sdk/src/types/storage.ts b/packages/sdk/src/types/storage.ts index 99336e3..e47ddae 100644 --- a/packages/sdk/src/types/storage.ts +++ b/packages/sdk/src/types/storage.ts @@ -68,11 +68,11 @@ export interface IPNSListRequest extends IApillonPagination { /** * IPNS name, that is used to access ipns content on ipfs gateway */ - ipnsName: string; + ipnsName?: string; /** * IPFS value (CID), to which this ipns points */ - ipnsValue: string; + ipnsValue?: string; } export interface ICreateIpns { From dc7065097e1fd609bebf50968e1b687a0ee47357 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Wed, 6 Dec 2023 08:59:31 +0100 Subject: [PATCH 09/15] Storage delete directory command --- .../src/modules/hosting/hosting.service.ts | 4 +--- .../src/modules/storage/storage.commands.ts | 21 +++++++++++++------ .../src/modules/storage/storage.service.ts | 16 +++++++++++++- .../src/modules/hosting/hosting-website.ts | 11 +++++----- 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/packages/cli/src/modules/hosting/hosting.service.ts b/packages/cli/src/modules/hosting/hosting.service.ts index 810e8ab..4498ad0 100644 --- a/packages/cli/src/modules/hosting/hosting.service.ts +++ b/packages/cli/src/modules/hosting/hosting.service.ts @@ -45,9 +45,7 @@ export async function deployWebsite( ); console.log(`Deployment started!`); - const deploymentData = await website - .deployment(deployment.deploymentUuid) - .get(); + const deploymentData = await website.deployment(deployment.uuid).get(); console.log(deploymentData.serialize()); } catch (err) { exceptionHandler(err); diff --git a/packages/cli/src/modules/storage/storage.commands.ts b/packages/cli/src/modules/storage/storage.commands.ts index 59cac01..23c79c6 100644 --- a/packages/cli/src/modules/storage/storage.commands.ts +++ b/packages/cli/src/modules/storage/storage.commands.ts @@ -7,9 +7,11 @@ import { uploadFromFolder, getFile, deleteFile, + deleteDirectory, } from './storage.service'; import { FileStatus } from '@apillon/sdk'; import { enumValues } from '../../lib/utils'; +import { createIpnsCommands } from './ipns.commands'; export function createStorageCommands(cli: Command) { const storage = cli @@ -18,6 +20,8 @@ export function createStorageCommands(cli: Command) { 'Commands for manipulating buckets and files on Apillon storage.', ); + createIpnsCommands(storage); + const listBucketsCommand = storage .command('list-buckets') .description('List project buckets') @@ -83,10 +87,15 @@ export function createStorageCommands(cli: Command) { await deleteFile(this.optsWithGlobals()); }); - /* - TODO: - - download file - - upload folder - - ipns methods - */ + storage + .command('delete-directory') + .description('Delete a directory from a storage bucket') + .requiredOption('-b, --bucket-uuid ', 'UUID of bucket') + .requiredOption( + '-d, --directory-uuid ', + 'UUID of directory to delete', + ) + .action(async function () { + await deleteDirectory(this.optsWithGlobals()); + }); } diff --git a/packages/cli/src/modules/storage/storage.service.ts b/packages/cli/src/modules/storage/storage.service.ts index 2f01adf..ce9f274 100644 --- a/packages/cli/src/modules/storage/storage.service.ts +++ b/packages/cli/src/modules/storage/storage.service.ts @@ -73,9 +73,23 @@ export async function deleteFile(optsWithGlobals: GlobalOptions) { try { await storage .bucket(optsWithGlobals.bucketUuid) - .deleteFile(optsWithGlobals.fileUuid); + .file(optsWithGlobals.fileUuid) + .delete(); console.log('File deleted successfully'); } catch (err) { exceptionHandler(err); } } + +export async function deleteDirectory(optsWithGlobals: GlobalOptions) { + const storage = new Storage(optsWithGlobals); + try { + await storage + .bucket(optsWithGlobals.bucketUuid) + .directory(optsWithGlobals.directoryUuid) + .delete(); + console.log('Directory deleted successfully'); + } catch (err) { + exceptionHandler(err); + } +} diff --git a/packages/sdk/src/modules/hosting/hosting-website.ts b/packages/sdk/src/modules/hosting/hosting-website.ts index 86ea268..eeeabca 100644 --- a/packages/sdk/src/modules/hosting/hosting-website.ts +++ b/packages/sdk/src/modules/hosting/hosting-website.ts @@ -92,7 +92,7 @@ export class HostingWebsite extends ApillonModel { /** * Deploy a website to a new environment. * @param {DeployToEnvironment} toEnvironment The environment to deploy to - * @returns The new deployment instance + * @returns {Deployment} */ public async deploy(toEnvironment: DeployToEnvironment) { ApillonLogger.log( @@ -104,14 +104,13 @@ export class HostingWebsite extends ApillonModel { ); ApillonLogger.logWithTime('Initiating deployment'); - const { data } = await ApillonApi.post>( - `${this.API_PREFIX}/deploy`, - { environment: toEnvironment }, - ); + const { data } = await ApillonApi.post< + IApillonResponse + >(`${this.API_PREFIX}/deploy`, { environment: toEnvironment }); ApillonLogger.logWithTime('Deployment in progress'); - return data; + return new Deployment(this.uuid, data.deploymentUuid, data); } /** From 12e1c74d757bd477b372b08d112be61bef2f3232 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Wed, 6 Dec 2023 09:27:58 +0100 Subject: [PATCH 10/15] Implement IPNS CLI commands --- packages/cli/README.md | 61 ++++++++++++++++ .../cli/src/modules/storage/ipns.commands.ts | 62 ++++++++++++++++ .../cli/src/modules/storage/ipns.service.ts | 71 +++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 packages/cli/src/modules/storage/ipns.commands.ts create mode 100644 packages/cli/src/modules/storage/ipns.service.ts diff --git a/packages/cli/README.md b/packages/cli/README.md index d3f9a8c..988d5ea 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -366,6 +366,67 @@ Deletes a specific file from a bucket. apillon storage delete-file --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --file-uuid "file_uuid_or_cid" ``` +### IPNS Commands + +#### `storage ipns list` +Lists all IPNS records for a specific bucket. + +**Options** +- `-b, --bucket-uuid `: UUID of the bucket. + +**Example** +```sh +apillon ipns list --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" +``` + +#### `storage ipns create` +Creates a new IPNS record for a specific bucket. + +**Options** +- `-b, --bucket-uuid `: UUID of the bucket. +- `-n, --name `: Name of the IPNS record. +- `-d, --description `: Description of the IPNS record (optional). +- `-c, --cid `: CID to which this IPNS name will point. + +**Example** +```sh +apillon ipns create --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --name "my-ipns-record" --cid "QmWX5CcNvnaVmgGBn4o82XW9uW1uLvsHQDdNrANrQeSdXm" +``` + +#### `storage ipns get` +Retrieves information about a specific IPNS record. + +**Options** +- `-i, --ipns-uuid `: UUID of the IPNS record. + +**Example** +```sh +apillon ipns get --ipns-uuid "123e4567-e89b-12d3-a456-426655440000" +``` + +#### `storage ipns publish` +Publishes an IPNS record to IPFS and links it to a CID. + +**Options** +- `-i, --ipns-uuid `: UUID of the IPNS record. +- `-c, --cid `: CID to which this IPNS name will point. + +**Example** +```sh +apillon ipns publish --ipns-uuid "123e4567-e89b-12d3-a456-426655440000" --cid "QmWX5CcNvnaVmgGBn4o82XW9uW1uLvsHQDdNrANrQeSdXm" +``` + +#### `storage ipns delete` +Deletes an IPNS record from a specific bucket. + +**Options** +- `-i, --ipns-uuid `: UUID of the IPNS record. + +**Example** +```sh +apillon ipns delete --ipns-uuid "123e4567-e89b-12d3-a456-426655440000" +``` + ## NFT Commands #### `nfts list-collections` diff --git a/packages/cli/src/modules/storage/ipns.commands.ts b/packages/cli/src/modules/storage/ipns.commands.ts new file mode 100644 index 0000000..aded531 --- /dev/null +++ b/packages/cli/src/modules/storage/ipns.commands.ts @@ -0,0 +1,62 @@ +import { Command } from 'commander'; +import { + listIpnsNames, + createIpns, + getIpns, + publishIpns, + deleteIpns, +} from './ipns.service'; + +export function createIpnsCommands(storageCli: Command) { + const ipns = storageCli + .command('ipns') + .requiredOption('-b, --bucket-uuid ', 'UUID of bucket') + .description( + 'Subcommands for manipulating IPNS records in a storage bucket', + ); + + ipns + .command('list') + .description('List all IPNS records for this bucket') + .action(async function () { + await listIpnsNames(this.optsWithGlobals()); + }); + + ipns + .command('create') + .description('Create a new IPNS record for this bucket') + .requiredOption('-n, --name ', 'Name of the IPNS record') + .option('-d, --description ', 'Description of the IPNS record') + .option('-c, --cid ', 'CID to which this IPNS name will point') + .action(async function () { + await createIpns(this.optsWithGlobals()); + }); + + ipns + .command('get') + .description('Get IPNS details') + .requiredOption('-i, --ipns-uuid ', 'UUID of the IPNS record') + .action(async function () { + await getIpns(this.optsWithGlobals()); + }); + + ipns + .command('publish') + .description('Publish an IPNS record to IPFS and link it to a CID') + .requiredOption('-i, --ipns-uuid ', 'UUID of the IPNS record') + .requiredOption( + '-c, --cid ', + 'CID to which this IPNS name will point', + ) + .action(async function () { + await publishIpns(this.optsWithGlobals()); + }); + + ipns + .command('delete') + .description('Delete an IPNS record from the bucket') + .requiredOption('-i, --ipns-uuid ', 'UUID of the IPNS record') + .action(async function () { + await deleteIpns(this.optsWithGlobals()); + }); +} diff --git a/packages/cli/src/modules/storage/ipns.service.ts b/packages/cli/src/modules/storage/ipns.service.ts new file mode 100644 index 0000000..09eb83e --- /dev/null +++ b/packages/cli/src/modules/storage/ipns.service.ts @@ -0,0 +1,71 @@ +import { Storage, exceptionHandler } from '@apillon/sdk'; +import { GlobalOptions } from '../../lib/types'; +import { paginate } from '../../lib/options'; + +export async function listIpnsNames(optsWithGlobals: GlobalOptions) { + const storage = new Storage(optsWithGlobals); + try { + const data = await storage + .bucket(optsWithGlobals.bucketUuid) + .listIpnsNames(paginate(optsWithGlobals)); + data.items = data.items.map((w) => w.serialize()); + console.log(data); + } catch (err) { + exceptionHandler(err); + } +} + +export async function createIpns(optsWithGlobals: GlobalOptions) { + const storage = new Storage(optsWithGlobals); + try { + const ipns = await storage.bucket(optsWithGlobals.bucketUuid).createIpns({ + name: optsWithGlobals.name, + description: optsWithGlobals.description, + cid: optsWithGlobals.cid, + }); + console.log('IPNS record created successfully'); + console.log(ipns.serialize()); + } catch (err) { + exceptionHandler(err); + } +} + +export async function getIpns(optsWithGlobals: GlobalOptions) { + const storage = new Storage(optsWithGlobals); + try { + const ipns = await storage + .bucket(optsWithGlobals.bucketUuid) + .ipns(optsWithGlobals.ipnsUuid) + .get(); + console.log(ipns.serialize()); + } catch (err) { + exceptionHandler(err); + } +} + +export async function publishIpns(optsWithGlobals: GlobalOptions) { + const storage = new Storage(optsWithGlobals); + try { + const ipns = await storage + .bucket(optsWithGlobals.bucketUuid) + .ipns(optsWithGlobals.ipnsUuid) + .publish(optsWithGlobals.cid); + console.log('IPNS published successfully'); + console.log(ipns.serialize()); + } catch (err) { + exceptionHandler(err); + } +} + +export async function deleteIpns(optsWithGlobals: GlobalOptions) { + const storage = new Storage(optsWithGlobals); + try { + await storage + .bucket(optsWithGlobals.bucketUuid) + .ipns(optsWithGlobals.ipnsUuid) + .delete(); + console.log('IPNS record deleted successfully'); + } catch (err) { + exceptionHandler(err); + } +} From 0cae514ae67926c78492922543fd3ce8d720638d Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Wed, 6 Dec 2023 09:39:47 +0100 Subject: [PATCH 11/15] Update CLI README --- packages/cli/README.md | 159 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 140 insertions(+), 19 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index 988d5ea..b2fe60c 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -31,7 +31,6 @@ npx @apillon/cli [options] > Note that when running without installation, you have to use `@apillon/cli` instead of `apillon` execution command. - ### Global Options - `--key `: Apillon API key (can be set via the `APILLON_API_KEY` environment variable). @@ -58,7 +57,7 @@ apillon hosting -h npx @apillon/cli hosting deploy-website --help ``` -### Global list pagination options +### List pagination options For commands that return a list of results, for example `apillon storage list-files`, or `apillon hosting list-websites`, there are global list pagination options that are available to use: @@ -76,14 +75,17 @@ The Apillon CLI currently supports the following commands: ## Hosting #### `hosting list-websites` + Lists all websites associated with your project. **Example** + ```sh apillon hosting list-websites --search "My-Website" --limit 1 ``` **Example response** + ```json { "items": [ @@ -95,8 +97,8 @@ apillon hosting list-websites --search "My-Website" --limit 1 "description": "My own website", "domain": "https://my-website.com", "bucketUuid": "47251013-37c6-4b30-be2b-8583dea25c4c", - "ipnsStaging": "k2k4r8ob2rf35wbmhhtzbq6nd4lhwv4qphwv9zl5smbzkuakwd50m6fd", - "ipnsProduction": "k2k4r8pple7phwm9azqgxshxdzyb1fs4n1hy8k5kcq5bkm5jnpznthrb" + "ipnsStaging": "k2k4r8ob2rf35wbmhhtzbq6nd4lhwv...", + "ipnsProduction": "k2k4r8pple7phwm9azqgxshxdzy..." }, ... ], @@ -105,49 +107,61 @@ apillon hosting list-websites --search "My-Website" --limit 1 ``` #### `hosting get-website` + Retrieves information about a specific website. **Options** + - `--uuid `: UUID of the website to get details for. **Example** + ```sh apillon hosting get-website --uuid "123e4567-e89b-12d3-a456-426655440000" ``` #### `hosting deploy-website` + Deploys a website from a local folder directly to Apillon hosting production environment. **Options** + - ``: Path to the folder containing your website files. - `--uuid `: UUID of the website to upload files to. - `-p, --preview`: Deploy to staging environment instead. **Example** + ```sh apillon hosting deploy-website ./public_html --uuid "123e4567-e89b-12d3-a456-426655440000" -p ``` #### `hosting upload` + Uploads a local folder's contents to a website deployment bucket. **Options** + - ``: Path to the folder containing your website files. - `--uuid `: UUID of the website to upload files to. **Example** + ```sh apillon hosting upload ./public_html --uuid "123e4567-e89b-12d3-a456-426655440000" ``` #### `hosting start-deployment` + Deploys a website to the specified environment, from files already uploaded to the hosting bucket. **Options** + - `--uuid `: UUID of the website to deploy. - `--env `: The environment to deploy to. Available choices: + ``` TO_STAGING = 1 STAGING_TO_PRODUCTION = 2 @@ -155,18 +169,22 @@ DIRECTLY_TO_PRODUCTION = 3 ``` **Example** + ```sh apillon hosting start-deployment --uuid "123e4567-e89b-12d3-a456-426655440000" --env 1 ``` #### `hosting list-deployments` + Lists all deployments for a specific website. **Options** + - `--uuid `: UUID of the website to list deployments for. - `--status `: The status of the deployments (DeploymentStatus enum, optional). Available choices: + ``` INITIATED = 0 IN_PROCESS = 1 @@ -177,17 +195,20 @@ FAILED = 100 - `--env `: The environment of the deployments (DeploymentStatus enum, optional). Available choices: + ``` STAGING = 2 PRODUCTION = 3 ``` **Example** + ```sh apillon hosting list-deployments --uuid "58a16026-1356-405b-97f9-efcc9dfac1dd" --order-by createTime --desc true ``` **Example response** + ```json { "items": [ @@ -197,7 +218,7 @@ apillon hosting list-deployments --uuid "58a16026-1356-405b-97f9-efcc9dfac1dd" - "uuid": "9b677fe2-1bb1-44d9-8956-e7749452f02d", "websiteUuid": "58a16026-1356-405b-97f9-efcc9dfac1dd", "cid": "QmPPBMsFccJVaLwvdhSh3zMbfEvonxoNSBLVd1kWK34Nps", - "cidv1": "bafybeizpqaa5xb5r46d2voj35qtokhb3c3bekofe5fnistbs7s3g7nnvmq", + "cidv1": "bafybeizpqaa5xb5r46d2voj35qtokhb3c3bekof...", "environment": "DIRECTLY_TO_PRODUCTION", "deploymentStatus": "SUCCESSFUL", "size": 7162, @@ -210,13 +231,16 @@ apillon hosting list-deployments --uuid "58a16026-1356-405b-97f9-efcc9dfac1dd" - ``` #### `hosting get-deployment` + Retrieves information about a specific deployment. **Options** + - `-w, --website-uuid `: UUID of the website. - `-d, --deployment-uuid `: UUID of the deployment **Example** + ```sh apillon hosting get-deployment --website-uuid "123e4567-e89b-12d3-a456-426655440000" --deployment-uuid "987e6543-e21c-32f1-b123-426655441111" ``` @@ -224,14 +248,17 @@ apillon hosting get-deployment --website-uuid "123e4567-e89b-12d3-a456-426655440 ## Storage Commands #### `storage list-buckets` + Lists all storage buckets associated with your project. **Example** + ```sh apillon storage list-buckets ``` **Example response** + ```json { "items": [ @@ -250,14 +277,17 @@ apillon storage list-buckets ``` #### `storage list-objects` + Retrieves objects (files and folders) from a specific bucket or bucket directory. **Options** + - `-b, --bucket-uuid `: UUID of the bucket to retrieve objects from. - `-d, --directory-uuid `: UUID of the directory to retrieve objects from (optional, default root folder). - `--deleted`: Include objects deleted from the bucket. **Example** + ```sh apillon storage list-objects --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --directory-uuid "987e6543-e21c-32f1-b123-426655441111" ``` @@ -273,11 +303,11 @@ apillon storage list-objects --bucket-uuid "123e4567-e89b-12d3-a456-426655440000 "uuid": "14a7a891-877c-41ac-900c-7382347e1e77", "name": "index.html", "CID": "QmWX5CcNvnaVmgGBn4o82XW9uW1uLvsHQDdNrANrQeSdXm", - "CIDv1": "bafybeidzrd7p5ddj67j2mud32cbnze2c7b2pvbhn7flfd22shnzuvgnima", + "CIDv1": "bafybeidzrd7p5ddj67j2mud32cbnze2c7b2pvbhn...", "status": "AVAILABLE_ON_IPFS_AND_REPLICATED", "directoryUuid": null, "type": "FILE", - "link": "https://ipfs-eu1.apillon.io/ipfs/bafybeidzrd7p5ddj67j2mud32cbnze2c7b2pvbhn7flfd22shnzuvgnima/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaWQiOiJiYWZ5YmVpZHpyZDdwNWRkajY3ajJtdWQzMmNibnplMmM3YjJwdmJobjdmbGZkMjJzaG56dXZnbmltYSIsInByb2plY3RfdXVpZCI6IjgwOWQzYjljLTMxNjMtNDgzMC1hZjg2LTJiNTMxYmZmNTUyZCIsImlhdCI6MTcwMDczNjM5Mywic3ViIjoiSVBGUy10b2tlbiJ9.nmq7BYdEYGXW6kHO9_ExOr3i5OBesWkN4TDI4QG6Fok", + "link": "https://ipfs.apillon.io/ipfs/bafybeidzrd7p5ddj67j...", "path": null, "bucketUuid": "91c57d55-e8e4-40b7-ad6a-81a82831bfb3" }, @@ -288,13 +318,16 @@ apillon storage list-objects --bucket-uuid "123e4567-e89b-12d3-a456-426655440000 ``` #### `storage list-files` + Retrieves files from a specific bucket. **Options** + - `-b, --bucket-uuid `: UUID of the bucket to retrieve files from. - `-s, --file-status `: Filter by file status (FileStatus enum, optional). Available choices: + ``` UPLOAD_REQUEST_GENERATED = 1 UPLOADED = 2 @@ -303,11 +336,13 @@ AVAILABLE_ON_IPFS_AND_REPLICATED = 4 ``` **Example** + ```sh apillon storage list-files --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" -s 2 ``` **Example response** + ```json { "items": [ @@ -316,11 +351,11 @@ apillon storage list-files --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" "updateTime": "2023-11-15T09:58:10.000Z", "name": "style.css", "CID": "QmWX5CcNvnaVmgGBn4o82XW9uW1uLvsHQDdNrANrQeSdXm", - "CIDv1": "bafybeidzrd7p5ddj67j2mud32cbnze2c7b2pvbhn7flfd22shnzuvgnima", + "CIDv1": "bafybeidzrd7p5ddj67j2mud32cbnze2c7b2pvbag...", "status": "AVAILABLE_ON_IPFS_AND_REPLICATED", "directoryUuid": null, "type": "FILE", - "link": "https://ipfs-eu1.apillon.io/ipfs/bafybeidzrd7p5ddj67j2mud32cbnze2c7b2pvbhn7flfd22shnzuvgnima/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaWQiOiJiYWZ5YmVpZHpyZDdwNWRkajY3ajJtdWQzMmNibnplMmM3YjJwdmJobjdmbGZkMjJzaG56dXZnbmltYSIsInByb2plY3RfdXVpZCI6IjgwOWQzYjljLTMxNjMtNDgzMC1hZjg2LTJiNTMxYmZmNTUyZCIsImlhdCI6MTcwMDczNjM5Mywic3ViIjoiSVBGUy10b2tlbiJ9.nmq7BYdEYGXW6kHO9_ExOr3i5OBesWkN4TDI4QG6Fok", + "link": "https://ipfs.apillon.io/ipfs/bafybeidzrd7p...", "path": null, "bucketUuid": "91c57d55-e8e4-40b7-ad6a-81a82831bfb3" }, @@ -331,41 +366,65 @@ apillon storage list-files --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" ``` #### `storage upload` + Upload contents of a local folder to specified bucket. **Options** + - ``: Path to the folder containing your files. - `-b, --bucket-uuid `: UUID of the bucket to upload files to. **Example** + ```sh apillon storage upload ./my_folder --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" ``` #### `storage get-file` + Retrieves information about a specific file in a bucket. **Options** + - `-b, --bucket-uuid `: UUID of the bucket. - `-f, --file-uuid `: UUID or CID of the file to retrieve. **Example** + ```sh apillon storage get-file --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --file-uuid "file_uuid_or_cid" ``` #### `storage delete-file` + Deletes a specific file from a bucket. **Options** + - `-b, --bucket-uuid `: UUID of the bucket. - `-f, --file-uuid `: UUID or CID of the file to delete. **Example** + ```sh apillon storage delete-file --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --file-uuid "file_uuid_or_cid" ``` +#### `storage delete-directory` + +Delete a directory from a storage bucket. + +**Options** + +- `-b, --bucket-uuid `: UUID of the bucket. +- `-d, --directory-uuid `: UUID of the directoru to delete. + +**Example** + +```sh +apillon storage delete-directory --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --directory-uuid "2c84048c-49a1-4ed2-9e1e-8920806ae968" +``` + ### IPNS Commands #### `storage ipns list` @@ -379,6 +438,38 @@ Lists all IPNS records for a specific bucket. apillon ipns list --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" ``` +**Example response** + +```json +{ + "items": [ + { + "createTime": "2023-12-06T12:29:09.000Z", + "updateTime": "2023-12-06T12:29:21.000Z", + "uuid": "80383a54-1d86-4761-a5e4-26a2fab474c9", + "name": "Images IPNS", + "description": "IPNS for images folder", + "ipnsName": "k2k4r8jp1jnlbe3qv...", + "ipnsValue": "/ipfs/QmUz4...", + "link": "https://ipfs.apillon.io/ipns/k2k4r8jp1jnlbe3qv...", + "bucketUuid": "a26184d7-acf5-4d6c-9195-465e3a7a5240" + }, + { + "createTime": "2023-12-06T12:29:37.000Z", + "updateTime": "2023-12-06T12:29:52.000Z", + "uuid": "2045db5b-b347-4ea6-a4c0-4445e071180d", + "name": "JSON IPNS", + "description": "IPNS for metadata folder", + "ipnsName": "k2k4r8opkl3i2zq7bin8lis4...", + "ipnsValue": "/ipfs/QmUz5Z6RcMynfZWoC...", + "link": "https://ipfs.apillon.io/ipns/k2k4r8opkl3i2z...", + "bucketUuid": "a26184d7-acf5-4d6c-9195-465e3a7a5240" + } + ], + "total": 2 +} +``` + #### `storage ipns create` Creates a new IPNS record for a specific bucket. @@ -430,12 +521,15 @@ apillon ipns delete --ipns-uuid "123e4567-e89b-12d3-a456-426655440000" ## NFT Commands #### `nfts list-collections` + Lists all NFT collections owned by the project related to the API key. **Options** + - `--status `: UUID of the collection to retrieve (CollectionStatus enum, optional). Available choices: + ``` CREATED = 0 DEPLOY_INITIATED = 1 @@ -446,6 +540,7 @@ FAILED = 5 ``` **Example** + ```sh apillon nfts list-collections --status 3 ``` @@ -486,85 +581,106 @@ apillon nfts list-collections --status 3 ``` #### `nfts get-collection` + Retrieves information about a specific NFT collection. **Options** + - `--uuid `: UUID of the collection to retrieve. **Example** + ```sh apillon nfts get-collection --uuid "123e4567-e89b-12d3-a456-426655440000" ``` #### `nfts create-collection` + Creates a new NFT collection. The JSON file needs to have the property structure as type `ICreateCollection`, which can be found in the [SDK docs](https://sdk-docs.apillon.io/interfaces/ICreateCollection.html). An example object can be also seen on the [NFT SDK docs](https://wiki.apillon.io/build/5-apillon-sdk.html#nfts). **Options** + - ``: Path to the JSON data file for the new collection. **Example** + ```sh apillon nfts create-collection ./nft-data.json ``` #### `nfts mint-nft` + Mints NFTs for a collection with a specific UUID. **Options** + - `--uuid `: UUID of the collection to mint NFTs to. - `-a, --address `: Address which will receive minted NFTs. - `-q --quantity `: Number of NFTs to mint. (default 1). **Example** + ```sh apillon nfts mint-nft --uuid "123e4567-e89b-12d3-a456-426655440000" --address "0xdAC17F958D2ee523a2206206994597C13D831ec7" --quantity 2 ``` #### `nfts nest-mint-nft` + Nest mints NFT child collection to a parent collection with a specific UUID and parent NFT with id. **Options** + - `-c, --parent-collection-uuid `: Parent collection UUID to which child NFTs will be minted to. - `-p, --parent-nft-id `: Parent collection NFT id to which child NFTs will be minted to. - `-q, --quantity `: Number of child NFTs to mint (default 1). **Example** + ```sh apillon nfts nest-mint-nft --parent-collection-uuid "123e4567-e89b-12d3-a456-426655440000" --parent-nft-id 5 --quantity 2 ``` #### `nfts burn-nft` + Burns an NFT for a collection with a specific UUID. **Options** + - `--uuid `: Collection UUID. - `-t, --token-id `: NFT id which will be burned. **Example** + ```sh apillon nfts burn-nft --uuid "123e4567-e89b-12d3-a456-426655440000" --token-id 123 ``` #### `nfts transfer-collection` + Transfers NFT collection ownership to a new wallet address. **Options** + - `--uuid `: Collection UUID. - `-a, --address `: Address which you want to transfer collection ownership to. **Example** + ```sh apillon nfts transfer-collection --uuid "123e4567-e89b-12d3-a456-426655440000" --address "0xdAC17F958D2ee523a2206206994597C13D831ec7" ``` #### `nfts list-transactions` + Lists NFT transactions for a specific collection UUID. **Options** + - `--uuid `: Collection UUID. - `--status `: Transaction status (TransactionStatus enum, optional). Available choices: + ``` PENDING = 1 CONFIRMED = 2 @@ -575,6 +691,7 @@ ERROR = 4 - `--type `: Transaction type (TransactionType enum, optional). Available choices: + ``` DEPLOY_CONTRACT = 1 TRANSFER_CONTRACT_OWNERSHIP = 2 @@ -585,6 +702,7 @@ NEST_MINT_NFT = 6 ``` **Example** + ```sh apillon nfts list-transactions --uuid "123e4567-e89b-12d3-a456-426655440000" ``` @@ -600,7 +718,7 @@ apillon nfts list-transactions --uuid "123e4567-e89b-12d3-a456-426655440000" "chainId": "MOONBEAM", "transactionType": "DEPLOY_CONTRACT", "transactionStatus": "CONFIRMED", - "transactionHash": "0xab99e630f9475df92768b1e5d73f43e291252d889dba81e8fcc0f0fbe690bc0b" + "transactionHash": "0xab99e630f9475df92768b1e5d73f4..." }, { "createTime": "2023-11-20T11:55:13.000Z", @@ -608,7 +726,7 @@ apillon nfts list-transactions --uuid "123e4567-e89b-12d3-a456-426655440000" "chainId": "MOONBEAM", "transactionType": "MINT_NFT", "transactionStatus": "CONFIRMED", - "transactionHash": "0x1ecfeeaeddfa0a39fc2ae1ec755d2736b2577866089fe1d619c84690fbdac05a" + "transactionHash": "0x1ecfeeaeddfa0a39fc2ae1ec755d27..." }, ... ], @@ -655,23 +773,24 @@ jobs: cp -r style dist/ cp -r js dist/ -#### -## if you are using a framework for building web app, you can replace previous two step with the -## appropriate command for generating static webpage, like an example bellow. -## Find the correct command in your framework documentation. You may need to to change the -## name of the source folder in the last step (CLI call) -#### + #### + ## if you are using a framework for building web app, you can replace previous two step with the + ## appropriate command for generating static webpage, like an example bellow. + ## Find the correct command in your framework documentation. You may need to to change the + ## name of the source folder in the last step (CLI call) + #### # - name: Build app # run: npm run build - - name: Deploy website env: APILLON_API_KEY: ${{ secrets.APILLON_API_KEY }} APILLON_API_SECRET: ${{ secrets.APILLON_API_SECRET }} WEBSITE_UUID: ${{ secrets.WEBSITE_UUID }} - run: npx --yes @apillon/cli hosting deploy-website ./dist --uuid $WEBSITE_UUID --key $APILLON_API_KEY --secret $APILLON_API_SECRET + run: | + npm i -g @apillon/cli + apillon hosting deploy-website ./dist --uuid $WEBSITE_UUID --key $APILLON_API_KEY --secret $APILLON_API_SECRET ``` In this example, the GitHub Actions workflow is triggered when a push event occurs on the master branch. The workflow performs the following steps: @@ -685,3 +804,5 @@ In this example, the GitHub Actions workflow is triggered when a push event occu Make sure to setup secret variables with the values from Apillon platform. That's it! You can now use this example as a starting point to deploy your website using the CLI tool in a CI/CD pipeline with GitHub Actions. + +You can also check a working example on [Github](https://github.com/Apillon/website-deploy-example/blob/main/.github/workflows/deploy.yml) From cf78ec8fe08d4444c769f2c62607e1b95bcdf2e2 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Wed, 6 Dec 2023 14:14:45 +0100 Subject: [PATCH 12/15] Update package version --- package-lock.json | 8 ++++---- package.json | 2 +- packages/cli/package.json | 2 +- packages/sdk/package.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b98c68..4f03c5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "apillon-web3-tools", - "version": "1.0.2", + "version": "1.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "apillon-web3-tools", - "version": "1.0.2", + "version": "1.1.0", "license": "MIT", "workspaces": [ "packages/*" @@ -6268,7 +6268,7 @@ }, "packages/cli": { "name": "@apillon/cli", - "version": "1.0.2", + "version": "1.1.0", "license": "MIT", "dependencies": { "@apillon/sdk": "*", @@ -6371,7 +6371,7 @@ }, "packages/sdk": { "name": "@apillon/sdk", - "version": "1.0.2", + "version": "1.1.0", "license": "MIT", "dependencies": { "axios": "^1.3.4" diff --git a/package.json b/package.json index a5972a1..4f560bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "apillon-web3-tools", - "version": "1.0.2", + "version": "1.1.0", "description": "Monorepo for Apillon tools", "author": "Apillon", "license": "MIT", diff --git a/packages/cli/package.json b/packages/cli/package.json index 78cad29..c701865 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@apillon/cli", "description": "▶◀ Apillon CLI tools ▶◀", - "version": "1.0.2", + "version": "1.1.0", "author": "Apillon", "license": "MIT", "main": "./dist/index.js", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index d479392..6359e66 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,7 +1,7 @@ { "name": "@apillon/sdk", "description": "▶◀ Apillon SDK for NodeJS ▶◀", - "version": "1.0.2", + "version": "1.1.0", "author": "Apillon", "license": "MIT", "main": "./dist/index.js", From a2becf26bdc9ca39ddd2ab1d1cfa122aa88f15f7 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Fri, 8 Dec 2023 09:00:09 +0100 Subject: [PATCH 13/15] Update README SDK examples --- packages/sdk/README.md | 53 ++++++++++++++++++++++++++-------- packages/sdk/src/docs-index.ts | 1 + 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 9209ad0..1cef525 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -89,7 +89,7 @@ const webpage1 = hosting.website("uuid"); await webpage1.get(); // Upload files from local folder -await webpage1.uploadFromFolder("./my-foler/files/"); +await webpage1.uploadFromFolder("./public"); // Or alternatively, send file buffers as upload parameters const htmlBuffer = fs.readFileSync("./public/index.html"); await webpage1.uploadFiles( @@ -97,12 +97,9 @@ await webpage1.uploadFiles( { fileName: "index.html", contentType: "text/html", - path: null, content: htmlBuffer, }, - ], - // Upload the files in a new subdirectory in the bucket instead of in the root of the bucket - { wrapWithDirectory: true, directoryPath: "main/subdir" } + ] ); // deploys uploaded files to staging environment @@ -147,20 +144,19 @@ await storage.listBuckets({ limit: 5 }); const bucket = storage.bucket("uuid"); // Upload files from local folder -await bucket.uploadFromFolder("./my-foler/files/"); +await bucket.uploadFromFolder("./my-folder/files/"); // Or alternatively, send file buffers as upload parameters -const htmlBuffer = fs.readFileSync("./public/index.html"); +const pdfBuffer = fs.readFileSync("./my-folder/files/document.pdf"); await bucket.uploadFiles( [ { - fileName: "index.html", - contentType: "text/html", - path: null, - content: htmlBuffer, + fileName: "document.pdf", + contentType: "application/pdf", + content: pdfBuffer, }, ], // Upload the files in a new subdirectory in the bucket instead of in the root of the bucket - { wrapWithDirectory: true, directoryPath: "main/subdir" } + { wrapWithDirectory: true, directoryPath: "main/documents" } ); // list objects (files, folders) in a bucket @@ -182,6 +178,39 @@ await bucket.file("2195521d-15cc-4f6e-abf2-13866f9c6e03").delete(); await bucket.directory("eddc52cf-92d2-436e-b6de-42d7cad621c3").delete(); ``` +### IPNS methods + +The Storage module additionally contains methods for manipulating IPNS records for a specific storage any. + +For detailed IPNS SDK method, class and property documentation visit [SDK IPNS docs](https://sdk-docs.apillon.io/classes/Ipns.html). + +```ts +import { Storage, LogLevel } from "@apillon/sdk"; + +const storage = new Storage({ + key: "yourApiKey", + secret: "yourApiSecret", + logLevel: LogLevel.NONE, +}); + +// create and instance of a bucket directly through uuid +const bucket = storage.bucket("uuid"); +// list all existing IPNS records +const ipnsNames = await bucket.listIpnsNames({ ipnsName: "Images IPNS" }); +// create a new IPNS record +const newIpns = await bucket.createIpns({ + name: "Music IPNS", + description: "IPNS for my music files", + cid: "QmS5NL2Rc6SCjFx7pvZHdTD8WGWjDt25WQskC7DsNKAatW", +}); +// Get an IPNS record"s details by UUID +const ipns = await bucket.ipns("ipns_uuid").get(); +// Publish an IPNS record to point to a given CID +await ipns.publish("QmajaeC15ZpcnjBpX4ARRBU127fpcZ2svYEfEBhFRkRZbN"); +// delete an IPNS record from the bucket +await ipns.delete(); +``` + ## NFTs NFT module encapsulates functionalities for NFT service available on Apillon dashboard. diff --git a/packages/sdk/src/docs-index.ts b/packages/sdk/src/docs-index.ts index 73d064a..d0a2113 100644 --- a/packages/sdk/src/docs-index.ts +++ b/packages/sdk/src/docs-index.ts @@ -7,6 +7,7 @@ export * from './modules/storage/storage'; export * from './modules/storage/storage-bucket'; export * from './modules/storage/file'; export * from './modules/storage/directory'; +export * from './modules/storage/ipns'; export * from './modules/hosting/hosting'; export * from './modules/hosting/deployment'; export * from './modules/hosting/hosting-website'; From f988afb622decc1ade15942ec80df1e22456b9f2 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Fri, 8 Dec 2023 10:05:41 +0100 Subject: [PATCH 14/15] Revert double quotes --- packages/sdk/README.md | 108 ++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 1cef525..a96c267 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -28,11 +28,11 @@ npm install @apillon/sdk ### Initialization ```ts -import { Hosting } from "@apillon/sdk"; +import { Hosting } from '@apillon/sdk'; const hosting = new Hosting({ - key: "", - secret: "", + key: '', + secret: '', }); ``` @@ -70,33 +70,33 @@ import { DeploymentStatus, Hosting, LogLevel, -} from "@apillon/sdk"; -import * as fs from "fs"; +} from '@apillon/sdk'; +import * as fs from 'fs'; const hosting = new Hosting({ - key: "yourApiKey", - secret: "yourApiSecret", + key: 'yourApiKey', + secret: 'yourApiSecret', logLevel: LogLevel.NONE, }); // list all websites -await hosting.listWebsites({ orderBy: "createTime" }); +await hosting.listWebsites({ orderBy: 'createTime' }); // create an instance of a website via uuid -const webpage1 = hosting.website("uuid"); +const webpage1 = hosting.website('uuid'); // gets website information await webpage1.get(); // Upload files from local folder -await webpage1.uploadFromFolder("./public"); +await webpage1.uploadFromFolder('./public'); // Or alternatively, send file buffers as upload parameters -const htmlBuffer = fs.readFileSync("./public/index.html"); +const htmlBuffer = fs.readFileSync('./public/index.html'); await webpage1.uploadFiles( [ { - fileName: "index.html", - contentType: "text/html", + fileName: 'index.html', + contentType: 'text/html', content: htmlBuffer, }, ] @@ -110,7 +110,7 @@ await webpage1.listDeployments(); // gets a specific deployment const deployment = await webpage1 - .deployment("3e0c66ea-317d-4e1f-bcd9-38026c3ea1ee") + .deployment('3e0c66ea-317d-4e1f-bcd9-38026c3ea1ee') .get(); // checks if deployment was successful @@ -128,12 +128,12 @@ For detailed storage SDK method, class and property documentation visit [SDK sto ### Usage example ```ts -import { Storage, LogLevel, FileStatus } from "@apillon/sdk"; -import * as fs from "fs"; +import { Storage, LogLevel, FileStatus } from '@apillon/sdk'; +import * as fs from 'fs'; const storage = new Storage({ - key: "yourApiKey", - secret: "yourApiSecret", + key: 'yourApiKey', + secret: 'yourApiSecret', logLevel: LogLevel.NONE, }); @@ -141,27 +141,27 @@ const storage = new Storage({ await storage.listBuckets({ limit: 5 }); // create and instance of a bucket directly through uuid -const bucket = storage.bucket("uuid"); +const bucket = storage.bucket('uuid'); // Upload files from local folder -await bucket.uploadFromFolder("./my-folder/files/"); +await bucket.uploadFromFolder('./my-folder/files/'); // Or alternatively, send file buffers as upload parameters -const pdfBuffer = fs.readFileSync("./my-folder/files/document.pdf"); +const pdfBuffer = fs.readFileSync('./my-folder/files/document.pdf'); await bucket.uploadFiles( [ { - fileName: "document.pdf", - contentType: "application/pdf", + fileName: 'document.pdf', + contentType: 'application/pdf', content: pdfBuffer, }, ], // Upload the files in a new subdirectory in the bucket instead of in the root of the bucket - { wrapWithDirectory: true, directoryPath: "main/documents" } + { wrapWithDirectory: true, directoryPath: 'main/documents' } ); // list objects (files, folders) in a bucket await bucket.listObjects({ - directoryUuid: "eaff2672-3012-46fb-9278-5efacc6cb616", + directoryUuid: 'eaff2672-3012-46fb-9278-5efacc6cb616', markedForDeletion: false, limit: 5, }); @@ -170,12 +170,12 @@ await bucket.listObjects({ await bucket.listFiles({ fileStatus: FileStatus.UPLOADED }); // gets a specific file in a bucket directly through uuid -const file = await bucket.file("2195521d-15cc-4f6e-abf2-13866f9c6e03").get(); +const file = await bucket.file('2195521d-15cc-4f6e-abf2-13866f9c6e03').get(); // deletes a file via uuid -await bucket.file("2195521d-15cc-4f6e-abf2-13866f9c6e03").delete(); +await bucket.file('2195521d-15cc-4f6e-abf2-13866f9c6e03').delete(); // deletes a directory via uuid -await bucket.directory("eddc52cf-92d2-436e-b6de-42d7cad621c3").delete(); +await bucket.directory('eddc52cf-92d2-436e-b6de-42d7cad621c3').delete(); ``` ### IPNS methods @@ -185,28 +185,28 @@ The Storage module additionally contains methods for manipulating IPNS records f For detailed IPNS SDK method, class and property documentation visit [SDK IPNS docs](https://sdk-docs.apillon.io/classes/Ipns.html). ```ts -import { Storage, LogLevel } from "@apillon/sdk"; +import { Storage, LogLevel } from '@apillon/sdk'; const storage = new Storage({ - key: "yourApiKey", - secret: "yourApiSecret", + key: 'yourApiKey', + secret: 'yourApiSecret', logLevel: LogLevel.NONE, }); // create and instance of a bucket directly through uuid -const bucket = storage.bucket("uuid"); +const bucket = storage.bucket('uuid'); // list all existing IPNS records -const ipnsNames = await bucket.listIpnsNames({ ipnsName: "Images IPNS" }); +const ipnsNames = await bucket.listIpnsNames({ ipnsName: 'Images IPNS' }); // create a new IPNS record const newIpns = await bucket.createIpns({ - name: "Music IPNS", - description: "IPNS for my music files", - cid: "QmS5NL2Rc6SCjFx7pvZHdTD8WGWjDt25WQskC7DsNKAatW", + name: 'Music IPNS', + description: 'IPNS for my music files', + cid: 'QmS5NL2Rc6SCjFx7pvZHdTD8WGWjDt25WQskC7DsNKAatW', }); -// Get an IPNS record"s details by UUID -const ipns = await bucket.ipns("ipns_uuid").get(); +// Get an IPNS record's details by UUID +const ipns = await bucket.ipns('ipns_uuid').get(); // Publish an IPNS record to point to a given CID -await ipns.publish("QmajaeC15ZpcnjBpX4ARRBU127fpcZ2svYEfEBhFRkRZbN"); +await ipns.publish('QmajaeC15ZpcnjBpX4ARRBU127fpcZ2svYEfEBhFRkRZbN'); // delete an IPNS record from the bucket await ipns.delete(); ``` @@ -230,11 +230,11 @@ import { LogLevel, Nft, TransactionStatus, -} from "@apillon/sdk"; +} from '@apillon/sdk'; const nft = new Nft({ - key: "yourApiKey", - secret: "yourApiSecret", + key: 'yourApiKey', + secret: 'yourApiSecret', logLevel: LogLevel.NONE, }); @@ -242,15 +242,15 @@ const nft = new Nft({ const collection1 = await nft.create({ collectionType: CollectionType.GENERIC, chain: EvmChain.MOONBEAM, - name: "SpaceExplorers", - symbol: "SE", - description: "A collection of unique space exploration NFTs.", - baseUri: "https://moonbeamnfts.com/collections/spaceexplorers/", - baseExtension: "json", + name: 'SpaceExplorers', + symbol: 'SE', + description: 'A collection of unique space exploration NFTs.', + baseUri: 'https://moonbeamnfts.com/collections/spaceexplorers/', + baseExtension: 'json', maxSupply: 1000, isRevokable: false, isSoulbound: false, - royaltiesAddress: "0x1234567890abcdef", + royaltiesAddress: '0x1234567890abcdef', royaltiesFees: 5, drop: true, dropStart: 1679875200, @@ -260,23 +260,23 @@ const collection1 = await nft.create({ // check if collection is deployed - available on chain if (collection1.collectionStatus == CollectionStatus.DEPLOYED) { - console.log("Collection deployed: ", collection1.transactionHash); + console.log('Collection deployed: ', collection1.transactionHash); } // search through collections -await nft.listCollections({ search: "My NFT" }); +await nft.listCollections({ search: 'My NFT' }); // create and instance of collection directly through uuid -const collection = await nft.collection("uuid").get(); +const collection = await nft.collection('uuid').get(); // mint a new nft in the collection -await collection.mint("0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", 1); +await collection.mint('0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD', 1); // nest mint a new nft if collection type is NESTABLE await collection.nestMint(collection.uuid, 1, 1); // burn/destroy a specific NFT by its ID if collection is set as revokable -await collection.burn("1"); +await collection.burn('1'); // list confirmed transactions on a collection await collection.listTransactions({ @@ -287,6 +287,6 @@ await collection.listTransactions({ // NOTE that this will disable the ability to mint/burn etc. from the SDK/API since only the owner // has this ability await collection.transferOwnership( - "0x5BA8B0c24bA5307b67E619ad500a635204F73bF1" + '0x5BA8B0c24bA5307b67E619ad500a635204F73bF1' ); ``` From 1dd369ba17ff331d74aaffc5bdcc556baa6939a9 Mon Sep 17 00:00:00 2001 From: Damjan Dimitrov Date: Fri, 8 Dec 2023 10:42:28 +0100 Subject: [PATCH 15/15] PR Review updates --- packages/cli/README.md | 10 ++++++---- packages/cli/src/modules/storage/ipns.commands.ts | 7 ++----- packages/sdk/README.md | 6 +++--- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index b2fe60c..9ca2409 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -72,7 +72,7 @@ For commands that return a list of results, for example `apillon storage list-fi The Apillon CLI currently supports the following commands: -## Hosting +## Hosting Commands #### `hosting list-websites` @@ -788,9 +788,11 @@ jobs: APILLON_API_KEY: ${{ secrets.APILLON_API_KEY }} APILLON_API_SECRET: ${{ secrets.APILLON_API_SECRET }} WEBSITE_UUID: ${{ secrets.WEBSITE_UUID }} - run: | - npm i -g @apillon/cli - apillon hosting deploy-website ./dist --uuid $WEBSITE_UUID --key $APILLON_API_KEY --secret $APILLON_API_SECRET + run: npx --yes @apillon/cli hosting deploy-website ./dist --uuid $WEBSITE_UUID --key $APILLON_API_KEY --secret $APILLON_API_SECRET + # Or alternatively you can use the run configuration below + # run: | + # npm i -g @apillon/cli + # apillon hosting deploy-website ./dist --uuid $WEBSITE_UUID --key $APILLON_API_KEY --secret $APILLON_API_SECRET ``` In this example, the GitHub Actions workflow is triggered when a push event occurs on the master branch. The workflow performs the following steps: diff --git a/packages/cli/src/modules/storage/ipns.commands.ts b/packages/cli/src/modules/storage/ipns.commands.ts index aded531..ffbf6b1 100644 --- a/packages/cli/src/modules/storage/ipns.commands.ts +++ b/packages/cli/src/modules/storage/ipns.commands.ts @@ -27,7 +27,7 @@ export function createIpnsCommands(storageCli: Command) { .description('Create a new IPNS record for this bucket') .requiredOption('-n, --name ', 'Name of the IPNS record') .option('-d, --description ', 'Description of the IPNS record') - .option('-c, --cid ', 'CID to which this IPNS name will point') + .option('-c, --cid ', 'Targeted CID') .action(async function () { await createIpns(this.optsWithGlobals()); }); @@ -44,10 +44,7 @@ export function createIpnsCommands(storageCli: Command) { .command('publish') .description('Publish an IPNS record to IPFS and link it to a CID') .requiredOption('-i, --ipns-uuid ', 'UUID of the IPNS record') - .requiredOption( - '-c, --cid ', - 'CID to which this IPNS name will point', - ) + .requiredOption('-c, --cid ', 'Targeted CID') .action(async function () { await publishIpns(this.optsWithGlobals()); }); diff --git a/packages/sdk/README.md b/packages/sdk/README.md index a96c267..3a5191a 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -217,9 +217,9 @@ NFT module encapsulates functionalities for NFT service available on Apillon das For detailed NFT SDK method, class and property documentation visit [SDK NFT docs](https://sdk-docs.apillon.io/classes/Nft.html). -::: warning -When you transfer ownership of the collection to another account Apillon will lose the ability to perform actions in your name (mint, burn, etc.). Before you transfer ownership make sure you do not need those functionalities via Apillon anymore. -::: +> **Warning** +> When you transfer ownership of the collection to another account Apillon will lose the ability to perform actions in your name (mint, burn, etc.). Before you transfer ownership make sure you do not need those functionalities via Apillon anymore. + ### Usage example