Full CLI rework and repair

This commit is contained in:
Jesse Wierzbinski 2024-03-11 20:20:38 -10:00
parent cbc6f46103
commit 28c73bc62a
No known key found for this signature in database
9 changed files with 1739 additions and 1114 deletions

View file

@ -9,7 +9,7 @@ module.exports = {
parserOptions: {
project: "./tsconfig.json",
},
ignorePatterns: ["node_modules/", "dist/", ".eslintrc.cjs", "cli.ts"],
ignorePatterns: ["node_modules/", "dist/", ".eslintrc.cjs"],
plugins: ["@typescript-eslint"],
root: true,
rules: {

View file

@ -152,22 +152,19 @@ bun start
### Using the CLI
> [!WARNING]
> The CLI is currently broken due to unknown bugs that are actively being investigated. The following instructions are for when this is fixed.
Lysand includes a built-in CLI for managing the server. To use it, simply run the following command:
```bash
bun cli
bun cli help
```
If you are running a production build, you will need to run `bun run dist/cli.js` or `./entrypoint.sh cli` instead.
You can use the `help` command to see a list of available commands. These include creating users, deleting users and more.
You can use the `help` command to see a list of available commands. These include creating users, deleting users and more. Each command also has a `--help,-h` flag that you can use to see more information about the command.
#### Scripting with the CLI
Some CLI commands that return data as tables can be used in scripts. To do so, you can use the `--json` flag to output the data as JSON instead of a table, or even `--csv` to output the data as CSV. See `bun cli help` for more information.
Some CLI commands that return data as tables can be used in scripts. To convert them to JSON or CSV, some commands allow you to specify a `--format` flag that can be either `"json"` or `"csv"`. See `bun cli help` or `bun cli <command> -h` for more information.
Flags can be used in any order and anywhere in the script (except for the `bun cli` command itself). The command arguments themselves must be in the correct order, however.

BIN
bun.lockb

Binary file not shown.

2087
cli.ts

File diff suppressed because it is too large Load diff

View file

@ -84,11 +84,14 @@
"dependencies": {
"@aws-sdk/client-s3": "^3.461.0",
"@iarna/toml": "^2.2.5",
"@json2csv/plainjs": "^7.0.6",
"@prisma/client": "^5.6.0",
"blurhash": "^2.0.5",
"bullmq": "latest",
"chalk": "^5.3.0",
"cli-parser": "file:packages/cli-parser",
"cli-table": "^0.3.11",
"config-manager": "file:packages/config-manager",
"eventemitter3": "^5.0.1",
"extract-zip": "^2.0.1",
"html-to-text": "^9.0.5",
@ -100,7 +103,9 @@
"linkify-html": "^4.1.3",
"linkify-string": "^4.1.3",
"linkifyjs": "^4.1.3",
"log-manager": "file:packages/log-manager",
"marked": "latest",
"media-manager": "file:packages/media-manager",
"megalodon": "^9.1.1",
"meilisearch": "latest",
"merge-deep-ts": "^1.2.6",
@ -108,12 +113,8 @@
"oauth4webapi": "^2.4.0",
"prisma": "^5.6.0",
"prisma-redis-middleware": "^4.8.0",
"semver": "^7.5.4",
"sharp": "^0.33.0-rc.2",
"request-parser": "file:packages/request-parser",
"config-manager": "file:packages/config-manager",
"cli-parser": "file:packages/cli-parser",
"log-manager": "file:packages/log-manager",
"media-manager": "file:packages/media-manager"
"semver": "^7.5.4",
"sharp": "^0.33.0-rc.2"
}
}

View file

@ -117,7 +117,9 @@ export class CliBuilder {
prev.categories.length > current.categories.length ? prev : current
);
const argsWithoutCategories = revelantArgs.slice(command.categories.length);
const argsWithoutCategories = revelantArgs.slice(
command.categories.length
);
return await command.run(argsWithoutCategories);
}
@ -243,8 +245,6 @@ export class CliBuilder {
})
);
console.log(optimal_length)
for (const line of writeBuffer.split("\n")) {
const [left, right] = line.split("|");
if (!right) {
@ -261,6 +261,7 @@ export class CliBuilder {
type ExecuteFunction<T> = (
instance: CliCommand,
args: Partial<T>
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
) => Promise<number> | Promise<void> | number | void;
/**
@ -364,7 +365,7 @@ ${unpositionedArgs
currentParameter = null;
} else {
const positionedArgType = this.argTypes.find(
argType => argType.positioned
argType => argType.positioned && !parsedArgs[argType.name]
);
if (positionedArgType) {
parsedArgs[positionedArgType.name] = this.castArgValue(

View file

@ -113,7 +113,7 @@ describe("CliCommand", () => {
).toEqual(["value1", "value2"]);
});
it("should run the execute function with the parsed parameters", () => {
it("should run the execute function with the parsed parameters", async () => {
const mockExecute = jest.fn();
cliCommand = new CliCommand(
["category1", "category2"],
@ -142,7 +142,7 @@ describe("CliCommand", () => {
mockExecute
);
cliCommand.run([
await cliCommand.run([
"--arg1",
"value1",
"--arg2",
@ -159,7 +159,7 @@ describe("CliCommand", () => {
});
});
it("should work with a mix of positioned and non-positioned arguments", () => {
it("should work with a mix of positioned and non-positioned arguments", async () => {
const mockExecute = jest.fn();
cliCommand = new CliCommand(
["category1", "category2"],
@ -194,7 +194,7 @@ describe("CliCommand", () => {
mockExecute
);
cliCommand.run([
await cliCommand.run([
"--arg1",
"value1",
"--arg2",
@ -324,7 +324,7 @@ describe("CliBuilder", () => {
expect(cliBuilder.commands).not.toContain(mockCommand2);
});
it("should process args correctly", () => {
it("should process args correctly", async () => {
const mockExecute = jest.fn();
const mockCommand = new CliCommand(
["category1", "sub1"],
@ -339,7 +339,7 @@ describe("CliBuilder", () => {
mockExecute
);
cliBuilder.registerCommand(mockCommand);
cliBuilder.processArgs([
await cliBuilder.processArgs([
"./cli.ts",
"category1",
"sub1",

View file

@ -32,6 +32,24 @@ export class MediaBackend {
public backend: MediaBackendType
) {}
static async fromBackendType(
backend: MediaBackendType,
config: ConfigType
): Promise<MediaBackend> {
switch (backend) {
case MediaBackendType.LOCAL:
return new (await import("./backends/local")).LocalMediaBackend(
config
);
case MediaBackendType.S3:
return new (await import("./backends/s3")).S3MediaBackend(
config
);
default:
throw new Error(`Unknown backend type: ${backend as any}`);
}
}
public getBackendType() {
return this.backend;
}

View file

@ -30,6 +30,39 @@ describe("MediaBackend", () => {
expect(mediaBackend.getBackendType()).toEqual(MediaBackendType.S3);
});
describe("fromBackendType", () => {
it("should return a LocalMediaBackend instance for LOCAL backend type", async () => {
const backend = await MediaBackend.fromBackendType(
MediaBackendType.LOCAL,
mockConfig
);
expect(backend).toBeInstanceOf(LocalMediaBackend);
});
it("should return a S3MediaBackend instance for S3 backend type", async () => {
const backend = await MediaBackend.fromBackendType(
MediaBackendType.S3,
{
s3: {
endpoint: "localhost:4566",
region: "us-east-1",
bucket_name: "test-bucket",
access_key: "test-access",
public_url: "test",
secret_access_key: "test-secret",
},
} as ConfigType
);
expect(backend).toBeInstanceOf(S3MediaBackend);
});
it("should throw an error for unknown backend type", () => {
expect(
MediaBackend.fromBackendType("unknown" as any, mockConfig)
).rejects.toThrow("Unknown backend type: unknown");
});
});
it("should check if images should be converted", () => {
expect(mediaBackend.shouldConvertImages(mockConfig)).toBe(true);
mockConfig.media.conversion.convert_images = false;