Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI Vaults w/ Environment Syncing, PeerJS Worker, and Misc Cleanup #66

Merged
merged 4 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

In order to run the CLI, you need the following:

- `node`: `>=18`
- `node`: `>=20`
- `npm`: `>=9` OR `yarn`: `^1`

## Installation
Expand Down
12 changes: 6 additions & 6 deletions cli/actions/grab.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { createGrabHandlers } from '@shared/handlers/grab';
import {
grabMachine,
initGrabContext,
grabMachine,
initGrabContext,
} from '@shared/lib/machines/grab';
import { DropContext } from '@shared/types/drop';
import { AnyGrabEvent, GrabContext } from '@shared/types/grab';
import { decryptFile, hashFile } from 'lib/crypto';
import {
displayWelcomeMessage,
logDebug,
logError,
logInfo,
displayWelcomeMessage,
logDebug,
logError,
logInfo,
} from 'lib/log';
import { initPeer } from 'lib/peer';
import { cleanupSession } from 'lib/session';
Expand Down
4 changes: 2 additions & 2 deletions cli/actions/secret/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export async function secretAdd(name: string, value: string) {

const { vaults, active_vault } = config;

const { createSecret } = createSecretsHelpers(vaults[active_vault]);
const { addSecrets } = createSecretsHelpers(vaults[active_vault]);

await createSecret(name, value);
await addSecrets([{ name, value }]);

logInfo('secret added successfully!');
}
20 changes: 14 additions & 6 deletions cli/actions/vault/export.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { createSecretsHelpers } from 'db/secrets';
import { loadConfig } from 'lib/config';
import { syncEnv } from 'lib/env';
import { logInfo } from 'lib/log';
import { resolve } from 'path';
import { cwd } from 'process';

export async function vaultExport(vaultNameInput: string) {
// TODO format support (.env, JSON files)
export async function vaultExport(
vaultNameInput: string,
envDestinationPath: string,
) {
const { config } = await loadConfig();

const { vaults, active_vault } = config;
Expand All @@ -16,13 +23,14 @@ export async function vaultExport(vaultNameInput: string) {

const secrets = await getAllSecrets();
const secretsMap = secrets.reduce(
(prev, { name, value }) => ({
...prev,
[name]: value,
}),
{} as Record<string, string>,
(prev, { name, value }) => (prev += `${name}="${value}"\n`),
``,
);

logInfo(`Secrets retrieved for '${vaultNameInput}' vault!`);
console.log(secretsMap);

const fullEnvPath = resolve(cwd(), envDestinationPath);

console.log(fullEnvPath);
}
26 changes: 26 additions & 0 deletions cli/actions/vault/import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { vaultExists } from 'db/vaults';
import { loadConfig } from 'lib/config';
import { addEnvToVault } from 'lib/env';
import { logError, logInfo } from 'lib/log';
import { exit } from 'process';

export async function vaultImport(envPath: string) {
const { config } = await loadConfig();

const { vaults, active_vault } = config;

const { key } = vaults[active_vault];

const location = vaultExists(vaults, active_vault);

if (!location) {
logError('Default vault could not be found!');
return exit(1);
}

const newSecrets = await addEnvToVault(envPath, { key, location });

logInfo(
`${newSecrets.rowsAffected} secrets added to vault from '${envPath}'!`,
);
}
6 changes: 6 additions & 0 deletions cli/actions/vault/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from './create';
export * from './delete';
export * from './export';
export * from './import';
export * from './sync';
export * from './use';
36 changes: 36 additions & 0 deletions cli/actions/vault/sync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createSecretsHelpers } from 'db/secrets';
import { loadConfig } from 'lib/config';
import { syncEnv } from 'lib/env';
import { logInfo } from 'lib/log';
import { resolve } from 'path';
import { cwd } from 'process';

export async function vaultSync(
vaultNameInput: string,
envDestinationPath: string,
) {
const { config } = await loadConfig();

const { vaults, active_vault } = config;

const { location, key } = vaults[active_vault];

const { getAllSecrets } = createSecretsHelpers({
location,
key,
});

const secrets = await getAllSecrets();
const secretsMap = secrets.reduce(
(prev, { name, value }) => ({
...prev,
[name]: value,
}),
{},
);

logInfo(`Secrets synced to ./.env for '${active_vault}' vault!`);
console.log(secretsMap);

await syncEnv('./.env', secretsMap);
}
5 changes: 3 additions & 2 deletions cli/actions/vault/use.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { vaultExists } from 'db/vaults';
import { existsSync } from 'fs';
import { loadConfig, saveConfig } from 'lib/config';
import { logError, logInfo } from 'lib/log';
import { cwd, exit } from 'process';
import { DeadropConfig } from 'types/config';

export async function vaultUse(vaultNameInput: string) {
const { config } = await loadConfig();
Expand All @@ -24,7 +24,8 @@ export async function vaultUse(vaultNameInput: string) {
return exit(0);
}

const updatedConfig = {
const updatedConfig: DeadropConfig = {
...config,
active_vault: vaultNameInput,
vaults,
};
Expand Down
102 changes: 102 additions & 0 deletions cli/core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Command } from 'commander';
import { description, version } from '../package.json';
import init from 'actions/init';
import { drop } from 'actions/drop';
import { grab } from 'actions/grab';
import { secretAdd } from 'actions/secret/add';
import { secretRemove } from 'actions/secret/remove';
import {
vaultCreate,
vaultDelete,
vaultExport,
vaultImport,
vaultSync,
vaultUse,
} from 'actions/vault';

const deadrop = new Command();

deadrop.name('deadrop').description(description).version(version);

deadrop.command('init').action(init);

deadrop
.command('drop')
.description('drop a secret from a vault or in raw format')
.argument('[input]', 'secret to drop')
.option('-i, --input [input]', 'secret to drop')
.option('-f, --file', 'secret to drop is a file')
.action(drop);

deadrop
.command('grab')
.description('grab a secret with a drop ID')
.argument('<id>', 'drop session ID')
.action(grab);

// vault commands

const vaultRoot = deadrop
.command('vault')
.description('manage your vaults');

vaultRoot
.command('create')
.description(
'create a new vault, optionally specify its parent folder',
)
.argument('<name>', 'name of the vault')
.argument('[location]', 'folder location of the vault')
.action(vaultCreate);

vaultRoot
.command('use')
.description('change the current active vault deadrop is using')
.argument('<name>', 'name of the vault to switch to as active')
.action(vaultUse);

vaultRoot
.command('sync')
.description('sync the current active vault with .env file')
.action(vaultSync);

vaultRoot
.command('export')
.description('export all the secrets of the specified vault')
.argument('<name>', 'name of the vault to export')
.action(vaultExport);

vaultRoot
.command('import')
.description(
'import all the secrets of a given .env file to active vault',
)
.argument('<path>', 'path to the .env file')
.action(vaultImport);

vaultRoot
.command('delete')
.description(
`delete the specified vault's database and remove it from config`,
)
.argument('<name>', 'name of the vault to delete')
.action(vaultDelete);

// secrets commands

const secretRoot = deadrop
.command('secret')
.description('manage your secrets in active vault');

secretRoot
.command('add')
.argument('[name]', 'name of the secret')
.argument('[value]', 'value of the secret')
.action(secretAdd);

secretRoot
.command('remove')
.argument('[name]', 'name of the secret to remove')
.action(secretRemove);

export { deadrop };
12 changes: 6 additions & 6 deletions cli/db/init.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createClient } from '@libsql/client';
import { drizzle } from 'drizzle-orm/libsql';
import { migrate } from 'drizzle-orm/libsql/migrator'
import { migrate } from 'drizzle-orm/libsql/migrator';

export const initDB = (path: string, encryptionKey: string) => {
const client = createClient({
url: `file:${path}`,
encryptionKey,
});
const client = createClient({
url: `file:${path}`,
encryptionKey,
});

return drizzle(client);
return drizzle(client);
};
6 changes: 4 additions & 2 deletions cli/db/schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { text, sqliteTable } from 'drizzle-orm/sqlite-core';

export const secretsTable = sqliteTable('secrets', {
name: text('name').primaryKey(),
value: text('value').notNull(),
name: text('name').primaryKey(),
value: text('value').notNull(),
});

export type SecretsInput = typeof secretsTable.$inferInsert;
12 changes: 5 additions & 7 deletions cli/db/secrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,22 @@ import { VaultDBConfig } from 'types/config';
import { initDB } from './init';
import { eq } from 'drizzle-orm/expressions';
import { migrate } from 'drizzle-orm/libsql/migrator';
import { secretsTable } from './schema';
import { SecretsInput, secretsTable } from './schema';

export function createSecretsHelpers({
location,
key,
}: VaultDBConfig) {
console.log(location);
const db = initDB(location, key);

const runMigrations = () =>
migrate(db, { migrationsFolder: './db/migrations' });

const createSecret = async (name: string, value: string) => {
const addSecrets = async (inputs: SecretsInput[]) => {
const [newSecret] = await db
.insert(secretsTable)
.values({
name,
value,
})
.values(inputs)
.returning();

return newSecret;
Expand All @@ -42,7 +40,7 @@ export function createSecretsHelpers({

return {
runMigrations,
createSecret,
addSecrets,
getSecret,
removeSecret,
getAllSecrets,
Expand Down
14 changes: 7 additions & 7 deletions cli/drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { Config } from 'drizzle-kit';

export default {
schema: './db/schema.ts',
out: './db/migrations',
dialect: 'sqlite',
driver: 'turso',
dbCredentials: {
url: 'file:.deadrop/default.db',
},
schema: './db/schema.ts',
out: './db/migrations',
dialect: 'sqlite',
driver: 'turso',
dbCredentials: {
url: 'file:.deadrop/default.db',
},
} satisfies Config;
Loading
Loading