mirror of
https://github.com/morten-olsen/mini-loader.git
synced 2026-02-08 01:36:26 +01:00
feat: switched from worker API to fs based
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
"@rollup/plugin-sucrase": "^5.0.2",
|
||||
"@trpc/client": "^10.45.0",
|
||||
"commander": "^11.1.0",
|
||||
"env-paths": "^3.0.0",
|
||||
"inquirer": "^9.2.12",
|
||||
"ora": "^8.0.1",
|
||||
"rollup": "^4.9.4",
|
||||
|
||||
@@ -2,13 +2,20 @@ import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
|
||||
import superjson from 'superjson';
|
||||
import type { Runtime } from '@morten-olsen/mini-loader-server';
|
||||
import type { RootRouter } from '@morten-olsen/mini-loader-server';
|
||||
import { Context } from '../context/context.js';
|
||||
|
||||
const createClient = () => {
|
||||
const createClient = (context: Context) => {
|
||||
if (!context.host || !context.token) {
|
||||
throw new Error('Not signed in');
|
||||
}
|
||||
const client = createTRPCProxyClient<RootRouter>({
|
||||
transformer: superjson,
|
||||
links: [
|
||||
httpBatchLink({
|
||||
url: 'http://localhost:4500/trpc',
|
||||
url: `${context.host}/trpc`,
|
||||
headers: {
|
||||
authorization: `Bearer ${context.token}`,
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { Context } from '../../context/context.js';
|
||||
|
||||
const list = new Command('list');
|
||||
|
||||
@@ -20,8 +21,9 @@ list
|
||||
.option('-a, --limit <limit>', 'Limit', '1000')
|
||||
.action(async () => {
|
||||
const { runId, loadId, offset, limit } = list.opts();
|
||||
const context = new Context();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
return createClient(context);
|
||||
});
|
||||
const artifacts = await step('Getting artifacts', async () => {
|
||||
return await client.artifacts.find.query({
|
||||
|
||||
32
packages/cli/src/commands/artifacts/artifacts.pull.ts
Normal file
32
packages/cli/src/commands/artifacts/artifacts.pull.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { Context } from '../../context/context.js';
|
||||
import { dirname, resolve } from 'path';
|
||||
import { mkdir, writeFile } from 'fs/promises';
|
||||
|
||||
const pull = new Command('pull');
|
||||
|
||||
pull
|
||||
.description('Download artifact')
|
||||
.argument('<artifact-id>', 'Artifact ID')
|
||||
.argument('<file>', 'File to save')
|
||||
.action(async (id, file) => {
|
||||
const context = new Context();
|
||||
const target = resolve(file);
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient(context);
|
||||
});
|
||||
const artifact = await step('Getting artifact', async () => {
|
||||
const result = await client.artifacts.get.query(id);
|
||||
if (!result) {
|
||||
throw new Error('Artifact not found');
|
||||
}
|
||||
return result;
|
||||
});
|
||||
await mkdir(dirname(target), { recursive: true });
|
||||
const data = Buffer.from(artifact.data, 'base64').toString('utf-8');
|
||||
await writeFile(target, data, 'utf-8');
|
||||
});
|
||||
|
||||
export { pull };
|
||||
59
packages/cli/src/commands/artifacts/artifacts.remove.ts
Normal file
59
packages/cli/src/commands/artifacts/artifacts.remove.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { Context } from '../../context/context.js';
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
const remove = new Command('remove');
|
||||
|
||||
const toInt = (value?: string) => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return parseInt(value, 10);
|
||||
};
|
||||
|
||||
remove
|
||||
.alias('ls')
|
||||
.description('List logs')
|
||||
.option('-r, --run-id <runId>', 'Run ID')
|
||||
.option('-l, --load-id <loadId>', 'Load ID')
|
||||
.option('-o, --offset <offset>', 'Offset')
|
||||
.option('-a, --limit <limit>', 'Limit', '1000')
|
||||
.action(async () => {
|
||||
const { runId, loadId, offset, limit } = remove.opts();
|
||||
const context = new Context();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient(context);
|
||||
});
|
||||
const response = await step('Preparing to delete', async () => {
|
||||
return await client.artifacts.prepareRemove.query({
|
||||
runId,
|
||||
loadId,
|
||||
offset: toInt(offset),
|
||||
limit: toInt(limit),
|
||||
});
|
||||
});
|
||||
|
||||
if (!response.ids.length) {
|
||||
console.log('No logs to delete');
|
||||
return;
|
||||
}
|
||||
const { confirm } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: `Are you sure you want to delete ${response.ids.length} logs?`,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
|
||||
await step('Deleting artifacts', async () => {
|
||||
await client.artifacts.remove.mutate(response);
|
||||
});
|
||||
});
|
||||
|
||||
export { remove };
|
||||
@@ -1,7 +1,11 @@
|
||||
import { Command } from 'commander';
|
||||
import { list } from './artifacts.list.js';
|
||||
import { remove } from './artifacts.remove.js';
|
||||
import { pull } from './artifacts.pull.js';
|
||||
|
||||
const artifacts = new Command('artifacts');
|
||||
artifacts.addCommand(list);
|
||||
artifacts.addCommand(remove);
|
||||
artifacts.addCommand(pull);
|
||||
|
||||
export { artifacts };
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { Command } from 'commander';
|
||||
import inquerer from 'inquirer';
|
||||
import { Context } from '../../context/context.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
|
||||
const login = new Command('login');
|
||||
|
||||
login.description('Login to your account');
|
||||
login.action(async () => {
|
||||
const context = new Context();
|
||||
const { host, token } = await inquerer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'host',
|
||||
message: 'Enter the host of your server',
|
||||
default: 'http://localhost:4500',
|
||||
default: context.host ?? 'http://localhost:4500',
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
@@ -19,7 +22,25 @@ login.action(async () => {
|
||||
},
|
||||
]);
|
||||
|
||||
console.log(host, token);
|
||||
const healthResponse = await step('Getting auth status', async () => {
|
||||
return await fetch(`${host}/health`, {
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
if (!healthResponse.ok) {
|
||||
throw new Error('Invalid token');
|
||||
}
|
||||
const health = await healthResponse.json();
|
||||
if (!health.authorized) {
|
||||
throw new Error('Invalid token');
|
||||
}
|
||||
|
||||
await step('Saving login', async () => {
|
||||
await context.saveLogin(host, token);
|
||||
});
|
||||
});
|
||||
|
||||
export { login };
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { Context } from '../../context/context.js';
|
||||
|
||||
const list = new Command('list');
|
||||
|
||||
@@ -8,11 +9,12 @@ list
|
||||
.alias('ls')
|
||||
.description('List loads')
|
||||
.action(async () => {
|
||||
const context = new Context();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
return createClient(context);
|
||||
});
|
||||
const loads = step('Getting data', async () => {
|
||||
await client.loads.find.query({});
|
||||
const loads = await step('Getting data', async () => {
|
||||
return await client.loads.find.query({});
|
||||
});
|
||||
console.table(loads);
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import { resolve } from 'path';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { bundle } from '../../bundler/bundler.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { Context } from '../../context/context.js';
|
||||
|
||||
const push = new Command('push');
|
||||
|
||||
@@ -14,9 +15,10 @@ push
|
||||
.option('-ai, --auto-install', 'Auto install dependencies', false)
|
||||
.action(async (script) => {
|
||||
const opts = push.opts();
|
||||
const context = new Context();
|
||||
const location = resolve(script);
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
return createClient(context);
|
||||
});
|
||||
const code = await step('Bundling', async () => {
|
||||
return await bundle({ entry: location, autoInstall: opts.autoInstall });
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { Context } from '../../context/context.js';
|
||||
|
||||
const list = new Command('list');
|
||||
|
||||
@@ -22,8 +23,9 @@ list
|
||||
.option('-s, --sort <order>', 'Sort', 'desc')
|
||||
.action(async () => {
|
||||
const { runId, loadId, severities, offset, limit, order } = list.opts();
|
||||
const context = new Context();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
return createClient(context);
|
||||
});
|
||||
const logs = await step('Getting logs', async () => {
|
||||
return await client.logs.find.query({
|
||||
|
||||
63
packages/cli/src/commands/logs/logs.remove.ts
Normal file
63
packages/cli/src/commands/logs/logs.remove.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { Context } from '../../context/context.js';
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
const remove = new Command('remove');
|
||||
|
||||
const toInt = (value?: string) => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return parseInt(value, 10);
|
||||
};
|
||||
|
||||
remove
|
||||
.alias('ls')
|
||||
.description('List logs')
|
||||
.option('-r, --run-id <runId>', 'Run ID')
|
||||
.option('-l, --load-id <loadId>', 'Load ID')
|
||||
.option('--severities <severities...>', 'Severities')
|
||||
.option('-o, --offset <offset>', 'Offset')
|
||||
.option('-a, --limit <limit>', 'Limit', '1000')
|
||||
.option('-s, --sort <order>', 'Sort', 'desc')
|
||||
.action(async () => {
|
||||
const { runId, loadId, severities, offset, limit, order } = remove.opts();
|
||||
const context = new Context();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient(context);
|
||||
});
|
||||
const response = await step('Preparing to delete', async () => {
|
||||
return await client.logs.prepareRemove.query({
|
||||
runId,
|
||||
loadId,
|
||||
severities,
|
||||
offset: toInt(offset),
|
||||
limit: toInt(limit),
|
||||
order,
|
||||
});
|
||||
});
|
||||
|
||||
if (!response.ids.length) {
|
||||
console.log('No logs to delete');
|
||||
return;
|
||||
}
|
||||
const { confirm } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: `Are you sure you want to delete ${response.ids.length} logs?`,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
|
||||
await step('Deleting logs', async () => {
|
||||
await client.logs.remove.mutate(response);
|
||||
});
|
||||
});
|
||||
|
||||
export { remove };
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Command } from 'commander';
|
||||
import { list } from './logs.list.js';
|
||||
import { remove } from './logs.remove.js';
|
||||
|
||||
const logs = new Command('logs');
|
||||
logs.addCommand(list);
|
||||
logs.addCommand(remove);
|
||||
|
||||
export { logs };
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { Context } from '../../context/context.js';
|
||||
|
||||
const create = new Command('create');
|
||||
|
||||
@@ -8,8 +9,9 @@ create
|
||||
.description('Create a new run')
|
||||
.argument('load-id', 'Load ID')
|
||||
.action(async (loadId) => {
|
||||
const context = new Context();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
return createClient(context);
|
||||
});
|
||||
await step('Creating run', async () => {
|
||||
await client.runs.create.mutate({ loadId });
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { Context } from '../../context/context.js';
|
||||
|
||||
const list = new Command('create');
|
||||
const list = new Command('list');
|
||||
|
||||
list
|
||||
.alias('ls')
|
||||
.description('Find a run')
|
||||
.argument('[load-id]', 'Load ID')
|
||||
.action(async (loadId) => {
|
||||
const context = new Context();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
return createClient(context);
|
||||
});
|
||||
const runs = await step('Getting runs', async () => {
|
||||
return await client.runs.find.query({ loadId });
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { Context } from '../../context/context.js';
|
||||
|
||||
const list = new Command('list');
|
||||
|
||||
@@ -18,8 +19,9 @@ list
|
||||
.option('-a, --limit <limit>', 'Limit', '1000')
|
||||
.action(async () => {
|
||||
const { offset, limit } = list.opts();
|
||||
const context = new Context();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
return createClient(context);
|
||||
});
|
||||
const secrets = await step('Getting secrets', async () => {
|
||||
return await client.secrets.find.query({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { Context } from '../../context/context.js';
|
||||
|
||||
const remove = new Command('remove');
|
||||
|
||||
@@ -8,8 +9,9 @@ remove
|
||||
.alias('rm')
|
||||
.argument('<id>')
|
||||
.action(async (id) => {
|
||||
const context = new Context();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
return createClient(context);
|
||||
});
|
||||
await step('Removing', async () => {
|
||||
await client.secrets.remove.mutate({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Command } from 'commander';
|
||||
import { createClient } from '../../client/client.js';
|
||||
import { step } from '../../utils/step.js';
|
||||
import { Context } from '../../context/context.js';
|
||||
|
||||
const set = new Command('set');
|
||||
|
||||
@@ -8,8 +9,9 @@ set
|
||||
.argument('<id>')
|
||||
.argument('[value]')
|
||||
.action(async (id, value) => {
|
||||
const context = new Context();
|
||||
const client = await step('Connecting to server', async () => {
|
||||
return createClient();
|
||||
return createClient(context);
|
||||
});
|
||||
await step('Setting secret', async () => {
|
||||
await client.secrets.set.mutate({
|
||||
|
||||
50
packages/cli/src/context/context.ts
Normal file
50
packages/cli/src/context/context.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import envPaths from 'env-paths';
|
||||
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { mkdir } from 'fs/promises';
|
||||
import { dirname } from 'path';
|
||||
|
||||
type ContextValues = {
|
||||
host: string;
|
||||
token: string;
|
||||
};
|
||||
|
||||
class Context {
|
||||
#location: string;
|
||||
#config?: ContextValues;
|
||||
|
||||
constructor() {
|
||||
const paths = envPaths('dws');
|
||||
this.#location = paths.config;
|
||||
if (existsSync(this.#location)) {
|
||||
this.#config = JSON.parse(readFileSync(this.#location, 'utf-8'));
|
||||
}
|
||||
}
|
||||
|
||||
public get host() {
|
||||
return this.#config?.host;
|
||||
}
|
||||
|
||||
public get token() {
|
||||
return this.#config?.token;
|
||||
}
|
||||
|
||||
public saveLogin = (host: string, token: string) => {
|
||||
this.#config = {
|
||||
...(this.#config || {}),
|
||||
host,
|
||||
token,
|
||||
};
|
||||
this.save();
|
||||
};
|
||||
|
||||
public save = async () => {
|
||||
if (!this.#config) {
|
||||
return;
|
||||
}
|
||||
const json = JSON.stringify(this.#config);
|
||||
mkdir(dirname(this.#location), { recursive: true });
|
||||
writeFileSync(this.#location, json);
|
||||
};
|
||||
}
|
||||
|
||||
export { Context };
|
||||
@@ -4,10 +4,10 @@ const step = async <T>(message: string, fn: () => Promise<T>): Promise<T> => {
|
||||
const spinner = ora(message).start();
|
||||
try {
|
||||
const result = await fn();
|
||||
spinner.succeed();
|
||||
await spinner.succeed();
|
||||
return result;
|
||||
} catch (err) {
|
||||
spinner.fail();
|
||||
await spinner.fail();
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user