diff --git a/packages/core-kernel/src/application.ts b/packages/core-kernel/src/application.ts index 5954e26891..968fa5e73f 100644 --- a/packages/core-kernel/src/application.ts +++ b/packages/core-kernel/src/application.ts @@ -336,6 +336,15 @@ export class Application extends Container implements Kernel.IApplication { return this.resolve("event"); } + /** + * @readonly + * @type {Contracts.Kernel.IFilesystem} + * @memberof Application + */ + public get filesystem(): Contracts.Kernel.IFilesystem { + return this.resolve("filesystem"); + } + /** * @readonly * @type {Contracts.Database.IDatabaseService} diff --git a/packages/core-kernel/src/bootstrap/app/load-factories.ts b/packages/core-kernel/src/bootstrap/app/load-factories.ts index d8fac78a24..eb6f374b2d 100644 --- a/packages/core-kernel/src/bootstrap/app/load-factories.ts +++ b/packages/core-kernel/src/bootstrap/app/load-factories.ts @@ -1,5 +1,6 @@ import { CacheFactory } from "../../services/cache"; -import { LoggerFactory } from "../../services/log"; +import { FilesystemFactory } from "../../services/filesystem"; +import { LoggerFactory } from "../../services/logger"; import { QueueFactory } from "../../services/queue"; import { AbstractBootstrapper } from "../bootstrapper"; @@ -13,6 +14,7 @@ export class LoadFactories extends AbstractBootstrapper { * @memberof LoadFactories */ public async bootstrap(): Promise { + this.app.bind("factoryFilesystem", new FilesystemFactory(this.app)); this.app.bind("factoryLogger", new LoggerFactory(this.app)); this.app.bind("factoryCache", new CacheFactory(this.app)); this.app.bind("factoryQueue", new QueueFactory(this.app)); diff --git a/packages/core-kernel/src/bootstrap/app/load-services.ts b/packages/core-kernel/src/bootstrap/app/load-services.ts index 15cfadc39a..dac9ee6246 100644 --- a/packages/core-kernel/src/bootstrap/app/load-services.ts +++ b/packages/core-kernel/src/bootstrap/app/load-services.ts @@ -2,8 +2,10 @@ import { JsonObject } from "type-fest"; import { ConfigFactory, ConfigRepository } from "../../config"; import { IConfigAdapter } from "../../contracts/core-kernel"; import { EventDispatcher } from "../../services/events"; -import { LoggerFactory } from "../../services/log"; -import { ConsoleLogger } from "../../services/log/adapters/console"; +import { FilesystemFactory } from "../../services/filesystem"; +import { LocalAdapter } from "../../services/filesystem/adapters/local"; +import { LoggerFactory } from "../../services/logger"; +import { ConsoleLogger } from "../../services/logger/adapters/console"; import { AbstractBootstrapper } from "../bootstrapper"; /** @@ -16,6 +18,8 @@ export class LoadServices extends AbstractBootstrapper { * @memberof LoadServices */ public async bootstrap(): Promise { + await this.registerFilesystem(); + this.registerEventDispatcher(); await this.registerLogger(); @@ -23,6 +27,18 @@ export class LoadServices extends AbstractBootstrapper { this.registerConfigLoader(); } + /** + * @private + * @returns {Promise} + * @memberof LoadServices + */ + private async registerFilesystem(): Promise { + this.app.bind( + "filesystem", + await this.app.resolve("factoryFilesystem").make(new LocalAdapter()), + ); + } + /** * @private * @memberof LoadServices diff --git a/packages/core-kernel/src/contracts/core-kernel/application.ts b/packages/core-kernel/src/contracts/core-kernel/application.ts index ad9f181c1a..0717ddff37 100644 --- a/packages/core-kernel/src/contracts/core-kernel/application.ts +++ b/packages/core-kernel/src/contracts/core-kernel/application.ts @@ -5,6 +5,7 @@ import { IPeerService } from "../core-p2p"; import { IConnection } from "../core-transaction-pool"; import { IContainer } from "./container"; import { IEventDispatcher } from "./event-dispatcher"; +import { IFilesystem } from "./filesystem"; import { ILogger } from "./logger"; export interface IApplication extends IContainer { @@ -18,6 +19,11 @@ export interface IApplication extends IContainer { */ readonly events: IEventDispatcher; + /** + * Get an instance of the application filesystem. + */ + readonly filesystem: IFilesystem; + /** * Get an instance of the application database. */ diff --git a/packages/core-kernel/src/contracts/core-kernel/filesystem.ts b/packages/core-kernel/src/contracts/core-kernel/filesystem.ts new file mode 100644 index 0000000000..2033f9fb8f --- /dev/null +++ b/packages/core-kernel/src/contracts/core-kernel/filesystem.ts @@ -0,0 +1,123 @@ +import { IApplication } from "./application"; + +export interface IFilesystem { + /** + * Create a new instance of the filesystem. + * + * @param {IApplication} app + * @returns {Promise} + * @memberof IFilesystem + */ + make(app: IApplication): Promise; + + /** + * Determine if a file exists. + * + * @param {string} path + * @returns {Promise} + * @memberof IFilesystem + */ + exists(path: string): Promise; + + /** + * Get the contents of a file. + * + * @param {string} path + * @returns {Promise} + * @memberof IFilesystem + */ + get(path: string): Promise; + + /** + * Write the contents of a file. + * + * @param {string} path + * @param {string} contents + * @returns {Promise} + * @memberof IFilesystem + */ + put(path: string, contents: string): Promise; + + /** + * Delete the file at a given path. + * + * @param {string} path + * @returns {Promise} + * @memberof IFilesystem + */ + delete(path: string): Promise; + + /** + * Copy a file to a new location. + * + * @param {string} from + * @param {string} to + * @returns {Promise} + * @memberof IFilesystem + */ + copy(from: string, to: string): Promise; + + /** + * Move a file to a new location. + * + * @param {string} from + * @param {string} to + * @returns {Promise} + * @memberof IFilesystem + */ + move(from: string, to: string): Promise; + + /** + * Get the file size of a given file. + * + * @param {string} path + * @returns {Promise} + * @memberof IFilesystem + */ + size(path: string): Promise; + + /** + * Get the file's last modification time. + * + * @param {string} path + * @returns {Promise} + * @memberof IFilesystem + */ + lastModified(path: string): Promise; + + /** + * Get an array of all files in a directory. + * + * @param {string} directory + * @returns {Promise} + * @memberof IFilesystem + */ + files(directory: string): Promise; + + /** + * Get all of the directories within a given directory. + * + * @param {string} directory + * @returns {Promise} + * @memberof IFilesystem + */ + directories(directory: string): Promise; + + /** + * Create a directory. + * + * @param {*} path + * @returns {Promise} + * @memberof IFilesystem + */ + makeDirectory(path): Promise; + + /** + * Recursively delete a directory. + * + * @param {string} directory + * @returns {Promise} + * @memberof IFilesystem + */ + deleteDirectory(directory: string): Promise; +} diff --git a/packages/core-kernel/src/contracts/core-kernel/index.ts b/packages/core-kernel/src/contracts/core-kernel/index.ts index f4f4108d75..2ec857a3db 100644 --- a/packages/core-kernel/src/contracts/core-kernel/index.ts +++ b/packages/core-kernel/src/contracts/core-kernel/index.ts @@ -3,4 +3,5 @@ export * from "./cache"; export * from "./config"; export * from "./container"; export * from "./event-dispatcher"; +export * from "./filesystem"; export * from "./logger"; diff --git a/packages/core-kernel/src/services/filesystem/adapters/local.ts b/packages/core-kernel/src/services/filesystem/adapters/local.ts new file mode 100644 index 0000000000..05e5f2b703 --- /dev/null +++ b/packages/core-kernel/src/services/filesystem/adapters/local.ts @@ -0,0 +1,207 @@ +import { + copyFile, + ensureDir, + lstat, + move, + pathExists, + readdir, + readFile, + remove, + rmdir, + stat, + writeFile, +} from "fs-extra"; +import { resolve } from "path"; +import { IApplication, IFilesystem } from "../../../contracts/core-kernel"; + +export class LocalAdapter implements IFilesystem { + /** + * Create a new instance of the filesystem. + * + * @param {IApplication} app + * @returns {Promise} + * @memberof IFilesystem + */ + public async make(app: IApplication): Promise { + return this; + } + + /** + * Determine if a file exists. + * + * @param {string} path + * @returns {Promise} + * @memberof IFilesystem + */ + public async exists(path: string): Promise { + return pathExists(path); + } + + /** + * Get the contents of a file. + * + * @param {string} path + * @returns {Promise} + * @memberof IFilesystem + */ + public async get(path: string): Promise { + return readFile(path); + } + + /** + * Write the contents of a file. + * + * @param {string} path + * @param {string} contents + * @returns {Promise} + * @memberof IFilesystem + */ + public async put(path: string, contents: string): Promise { + try { + await writeFile(path, contents); + + return true; + } catch { + return false; + } + } + + /** + * Delete the file at a given path. + * + * @param {string} path + * @returns {Promise} + * @memberof IFilesystem + */ + public async delete(path: string): Promise { + try { + await remove(path); + + return true; + } catch { + return false; + } + } + + /** + * Copy a file to a new location. + * + * @param {string} from + * @param {string} to + * @returns {Promise} + * @memberof IFilesystem + */ + public async copy(from: string, to: string): Promise { + try { + await copyFile(from, to); + + return true; + } catch { + return false; + } + } + + /** + * Move a file to a new location. + * + * @param {string} from + * @param {string} to + * @returns {Promise} + * @memberof IFilesystem + */ + public async move(from: string, to: string): Promise { + try { + await move(from, to); + + return true; + } catch { + return false; + } + } + + /** + * Get the file size of a given file. + * + * @param {string} path + * @returns {Promise} + * @memberof IFilesystem + */ + public async size(path: string): Promise { + return (await stat(path)).size; + } + + /** + * Get the file's last modification time. + * + * @param {string} path + * @returns {Promise} + * @memberof IFilesystem + */ + public async lastModified(path: string): Promise { + return +(await stat(path)).mtime; + } + + /** + * Get an array of all files in a directory. + * + * @param {string} directory + * @returns {Promise} + * @memberof LocalAdapter + */ + public async files(directory: string): Promise { + directory = resolve(directory); + + return (await readdir(directory)) + .map((item: string) => `${directory}/${item}`) + .filter(async (item: string) => (await lstat(item)).isFile()); + } + + /** + * Get all of the directories within a given directory. + * + * @param {string} directory + * @returns {Promise[]} + * @memberof IFilesystem + */ + public async directories(directory: string): Promise { + directory = resolve(directory); + + return (await readdir(directory)) + .map((item: string) => `${directory}/${item}`) + .filter(async (item: string) => (await lstat(item)).isDirectory()); + } + + /** + * Create a directory. + * + * @param {*} path + * @returns {Promise} + * @memberof IFilesystem + */ + public async makeDirectory(path): Promise { + try { + await ensureDir(path); + + return true; + } catch { + return false; + } + } + + /** + * Recursively delete a directory. + * + * @param {string} directory + * @returns {Promise} + * @memberof IFilesystem + */ + public async deleteDirectory(directory: string): Promise { + try { + await rmdir(directory); + + return true; + } catch { + return false; + } + } +} diff --git a/packages/core-kernel/src/services/filesystem/factory.ts b/packages/core-kernel/src/services/filesystem/factory.ts new file mode 100644 index 0000000000..ecf5314eb4 --- /dev/null +++ b/packages/core-kernel/src/services/filesystem/factory.ts @@ -0,0 +1,22 @@ +import { Kernel } from "../../contracts"; + +/** + * @export + * @class FilesystemFactory + */ +export class FilesystemFactory { + /** + * @param {Kernel.IApplication} app + * @memberof FilesystemFactory + */ + public constructor(private readonly app: Kernel.IApplication) {} + + /** + * @param {Kernel.IFilesystem} driver + * @returns {Promise} + * @memberof FilesystemFactory + */ + public async make(driver: Kernel.IFilesystem): Promise { + return driver.make(this.app); + } +} diff --git a/packages/core-kernel/src/services/filesystem/index.ts b/packages/core-kernel/src/services/filesystem/index.ts new file mode 100644 index 0000000000..47d38b8071 --- /dev/null +++ b/packages/core-kernel/src/services/filesystem/index.ts @@ -0,0 +1 @@ +export * from "./factory"; diff --git a/packages/core-kernel/src/services/index.ts b/packages/core-kernel/src/services/index.ts index 47b59cbeb1..d79df5d83c 100644 --- a/packages/core-kernel/src/services/index.ts +++ b/packages/core-kernel/src/services/index.ts @@ -1,6 +1,6 @@ import * as Cache from "./cache"; import * as Events from "./events"; -import * as Log from "./log"; +import * as Log from "./logger"; import * as Queue from "./queue"; import * as Scheduler from "./scheduler"; diff --git a/packages/core-kernel/src/services/log/adapters/console.ts b/packages/core-kernel/src/services/logger/adapters/console.ts similarity index 100% rename from packages/core-kernel/src/services/log/adapters/console.ts rename to packages/core-kernel/src/services/logger/adapters/console.ts diff --git a/packages/core-kernel/src/services/log/factory.ts b/packages/core-kernel/src/services/logger/factory.ts similarity index 100% rename from packages/core-kernel/src/services/log/factory.ts rename to packages/core-kernel/src/services/logger/factory.ts diff --git a/packages/core-kernel/src/services/log/index.ts b/packages/core-kernel/src/services/logger/index.ts similarity index 100% rename from packages/core-kernel/src/services/log/index.ts rename to packages/core-kernel/src/services/logger/index.ts diff --git a/packages/core-kernel/src/services/log/logger.ts b/packages/core-kernel/src/services/logger/logger.ts similarity index 100% rename from packages/core-kernel/src/services/log/logger.ts rename to packages/core-kernel/src/services/logger/logger.ts diff --git a/packages/core/bin/config/testnet/service-providers.js b/packages/core/bin/config/testnet/service-providers.js index fe9c8b940d..cc37be0c62 100644 --- a/packages/core/bin/config/testnet/service-providers.js +++ b/packages/core/bin/config/testnet/service-providers.js @@ -1,61 +1,62 @@ module.exports = { - "@arkecosystem/core-logger-pino": {}, - "@arkecosystem/core-state": {}, - "@arkecosystem/core-database": {}, - "@arkecosystem/core-database-postgres": { - connection: { - host: process.env.CORE_DB_HOST || "localhost", - port: process.env.CORE_DB_PORT || 5432, - database: process.env.CORE_DB_DATABASE || `${process.env.CORE_TOKEN}_${process.env.CORE_NETWORK_NAME}`, - user: process.env.CORE_DB_USERNAME || process.env.CORE_TOKEN, - password: process.env.CORE_DB_PASSWORD || "password", - }, - }, - "@arkecosystem/core-transaction-pool": { - enabled: true, - maxTransactionsPerSender: process.env.CORE_TRANSACTION_POOL_MAX_PER_SENDER || 300, - allowedSenders: [], - dynamicFees: { - enabled: true, - minFeePool: 1000, - minFeeBroadcast: 1000, - addonBytes: { - transfer: 100, - secondSignature: 250, - delegateRegistration: 400000, - vote: 100, - multiSignature: 500, - ipfs: 250, - multiPayment: 500, - delegateResignation: 100, - htlcLock: 100, - htlcClaim: 0, - htlcRefund: 0, - }, - }, - }, - "@arkecosystem/core-p2p": { - server: { - port: process.env.CORE_P2P_PORT || 4000, - }, - minimumNetworkReach: 5, - }, - "@arkecosystem/core-blockchain": {}, - "@arkecosystem/core-api": { - enabled: !process.env.CORE_API_DISABLED, - host: process.env.CORE_API_HOST || "0.0.0.0", - port: process.env.CORE_API_PORT || 4003, - }, - "@arkecosystem/core-wallet-api": {}, - "@arkecosystem/core-webhooks": { - enabled: process.env.CORE_WEBHOOKS_ENABLED, - server: { - host: process.env.CORE_WEBHOOKS_HOST || "0.0.0.0", - port: process.env.CORE_WEBHOOKS_PORT || 4004, - whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], - }, - }, - "@arkecosystem/core-forger": {}, + "@arkecosystem/core-kernel-dummy": {}, + // "@arkecosystem/core-logger-pino": {}, + // "@arkecosystem/core-state": {}, + // "@arkecosystem/core-database": {}, + // "@arkecosystem/core-database-postgres": { + // connection: { + // host: process.env.CORE_DB_HOST || "localhost", + // port: process.env.CORE_DB_PORT || 5432, + // database: process.env.CORE_DB_DATABASE || `${process.env.CORE_TOKEN}_${process.env.CORE_NETWORK_NAME}`, + // user: process.env.CORE_DB_USERNAME || process.env.CORE_TOKEN, + // password: process.env.CORE_DB_PASSWORD || "password", + // }, + // }, + // "@arkecosystem/core-transaction-pool": { + // enabled: true, + // maxTransactionsPerSender: process.env.CORE_TRANSACTION_POOL_MAX_PER_SENDER || 300, + // allowedSenders: [], + // dynamicFees: { + // enabled: true, + // minFeePool: 1000, + // minFeeBroadcast: 1000, + // addonBytes: { + // transfer: 100, + // secondSignature: 250, + // delegateRegistration: 400000, + // vote: 100, + // multiSignature: 500, + // ipfs: 250, + // multiPayment: 500, + // delegateResignation: 100, + // htlcLock: 100, + // htlcClaim: 0, + // htlcRefund: 0, + // }, + // }, + // }, + // "@arkecosystem/core-p2p": { + // server: { + // port: process.env.CORE_P2P_PORT || 4000, + // }, + // minimumNetworkReach: 5, + // }, + // "@arkecosystem/core-blockchain": {}, + // "@arkecosystem/core-api": { + // enabled: !process.env.CORE_API_DISABLED, + // host: process.env.CORE_API_HOST || "0.0.0.0", + // port: process.env.CORE_API_PORT || 4003, + // }, + // "@arkecosystem/core-wallet-api": {}, + // "@arkecosystem/core-webhooks": { + // enabled: process.env.CORE_WEBHOOKS_ENABLED, + // server: { + // host: process.env.CORE_WEBHOOKS_HOST || "0.0.0.0", + // port: process.env.CORE_WEBHOOKS_PORT || 4004, + // whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + // }, + // }, + // "@arkecosystem/core-forger": {}, // "@arkecosystem/core-exchange-json-rpc": { // enabled: process.env.CORE_EXCHANGE_JSON_RPC_ENABLED, // host: process.env.CORE_EXCHANGE_JSON_RPC_HOST || "0.0.0.0",