Support uploading config files. #14
90
src/upload.ts
Normal file
90
src/upload.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import crypto from 'crypto';
|
||||
import fs from 'fs';
|
||||
import assert from 'node:assert';
|
||||
import openpgp from 'openpgp';
|
||||
import YAML from 'yaml';
|
||||
import { atob } from 'node:buffer';
|
||||
|
||||
import { Config } from './config.js';
|
||||
|
||||
let privateKey: openpgp.PrivateKey | null = null;
|
||||
|
||||
const loadPrivateKey = async () => {
|
||||
if (null == privateKey) {
|
||||
privateKey = await openpgp.decryptKey({
|
||||
privateKey: await openpgp.readPrivateKey({
|
||||
binaryKey: fs.readFileSync(Config.OPENPGP_PRIVATE_KEY_FILE)
|
||||
}),
|
||||
passphrase: Config.OPENPGP_PASSPHRASE,
|
||||
});
|
||||
}
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
const randomId = (): string =>
|
||||
crypto
|
||||
.randomUUID({ disableEntropyCache: true })
|
||||
.replaceAll('-', '')
|
||||
.toUpperCase();
|
||||
|
||||
const validateConfig = (obj): undefined => {
|
||||
assert(obj.authorized, "'authorized' is required");
|
||||
assert(Array.isArray(obj.authorized), "'authorized' must be an array");
|
||||
assert(obj.authorized.length >= 1, "'authorized' cannot be empty");
|
||||
assert(obj.config, "'config' is required");
|
||||
};
|
||||
|
||||
export const b64ToBytes = (base64): Uint8Array => {
|
||||
const binaryString = atob(base64);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const decrypt = async (binaryMessage: Uint8Array): Promise<any> => {
|
||||
const message = await openpgp.readMessage({
|
||||
binaryMessage,
|
||||
});
|
||||
|
||||
const { data } = await openpgp.decrypt({
|
||||
message,
|
||||
decryptionKeys: await loadPrivateKey(),
|
||||
});
|
||||
|
||||
const config = data.toString();
|
||||
return config.charAt(0) === '{' ? JSON.parse(config) : YAML.parse(config);
|
||||
};
|
||||
|
||||
export class Uploader {
|
||||
directory: string;
|
||||
|
||||
constructor(dir: string) {
|
||||
this.directory = dir;
|
||||
}
|
||||
|
||||
async upload(body: string | Uint8Array): Promise<string> {
|
||||
let raw: any;
|
||||
try {
|
||||
raw = b64ToBytes(body);
|
||||
} catch {
|
||||
raw = body;
|
||||
}
|
||||
const obj = await decrypt(raw);
|
||||
validateConfig(obj);
|
||||
|
||||
let id = randomId();
|
||||
let destination: string;
|
||||
do {
|
||||
id = randomId();
|
||||
destination = `${this.directory}/${id}`;
|
||||
} while (fs.existsSync(destination));
|
||||
|
||||
fs.writeFileSync(destination, raw);
|
||||
return id;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user