From ea3f50b1fb7ccb3b71cb0561954c9a81e81d5877 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 11 Jan 2026 20:29:07 +0800 Subject: [PATCH] feat: updates --- libraries/README.md | 3 +- libraries/deno-commons-mod.ts | 54 ++++++++++++++++++++++++++++----- libraries/deno-teencrypt-mod.ts | 45 +++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 libraries/deno-teencrypt-mod.ts diff --git a/libraries/README.md b/libraries/README.md index 94f29a9..7360b5f 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -3,12 +3,13 @@ Check scripts: ```shell cmd script check --type deno-mod +hatter script check --type deno-mod ``` Publish script: ```shell -cmd script pub --type deno-mod deno-commons-mod.ts +hatter script pub --type deno-mod deno-commons-mod.ts ``` diff --git a/libraries/deno-commons-mod.ts b/libraries/deno-commons-mod.ts index 6b38edb..994c266 100644 --- a/libraries/deno-commons-mod.ts +++ b/libraries/deno-commons-mod.ts @@ -6,7 +6,7 @@ import { assertEquals } from "jsr:@std/assert"; import { dirname } from "https://deno.land/std@0.208.0/path/mod.ts"; export async function sleep(timeoutMillis: number): Promise { - await new Promise(resolve => setTimeout(resolve, timeoutMillis)) + await new Promise((resolve) => setTimeout(resolve, timeoutMillis)); } export function compareVersion(ver1: string, ver2: string): 0 | 1 | -1 { @@ -236,6 +236,14 @@ class Logger { export const log = new Logger(); +export function getHomeDirOrDie(): string { + const homeDir = getHomeDir(); + if (homeDir === null) { + throw new Error("Cannot find home dir"); + } + return homeDir; +} + export function getHomeDir(): string | null { if (Deno.build.os === "windows") { const userProfile = Deno.env.get("USERPROFILE"); @@ -268,32 +276,62 @@ export async function existsPath(path: string): Promise { } } -export async function readFileToString(filename: string): Promise { +export async function readFileToString( + filename: string, +): Promise { try { return await Deno.readTextFile(resolveFilename(filename)); } catch (e) { - if (e instanceof Error && e.name == 'NotFound') { + if (e instanceof Error && e.name == "NotFound") { return null; } throw e; } } -export async function writeStringToFile(filename: string, data: string | null): Promise { +export async function writeStringToFile( + filename: string, + data: string | null, +): Promise { const newFilename = resolveFilename(filename); if (data == null) { if (await existsPath(newFilename)) { - await Deno.remove(newFilename) + await Deno.remove(newFilename); } } else { const parentDirname = dirname(newFilename); if (!await existsPath(parentDirname)) { - await Deno.mkdir(parentDirname, {recursive: true}); + await Deno.mkdir(parentDirname, { recursive: true }); } await Deno.writeTextFile(newFilename, data); } } +export function uint8ArrayToHexString(uint8: Uint8Array): string { + return Array.from(uint8) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); +} + +export function hexStringToUint8Array(hex: string): Uint8Array { + hex = hex.trim(); + if (hex.startsWith("0x") || hex.startsWith("0X")) { + hex = hex.slice(2); + } + if (hex.length % 2 !== 0) { + throw new Error("Hex string must have an even number of characters"); + } + if (!/^[0-9a-fA-F]*$/.test(hex)) { + throw new Error("Invalid hex string"); + } + const byteLength = hex.length / 2; + const uint8 = new Uint8Array(byteLength); + for (let i = 0; i < byteLength; i++) { + uint8[i] = parseInt(hex.substring(i * 2, (i + 1) * 2), 16); + } + return uint8; +} + Deno.test("isOn", () => { assertEquals(false, isOn(undefined)); assertEquals(false, isOn("")); @@ -354,7 +392,7 @@ Deno.test("formatPercent", () => { Deno.test("sleep", async () => { const t1 = new Date().getTime(); - await sleep(1000) + await sleep(1000); const t2 = new Date().getTime(); assert(Math.abs(1000 - (t2 - t1)) < 20); -}); \ No newline at end of file +}); diff --git a/libraries/deno-teencrypt-mod.ts b/libraries/deno-teencrypt-mod.ts new file mode 100644 index 0000000..1ab3cba --- /dev/null +++ b/libraries/deno-teencrypt-mod.ts @@ -0,0 +1,45 @@ +import { + getHomeDirOrDie, + hexStringToUint8Array, + uint8ArrayToHexString, +} from "https://global.hatter.ink/script/get/@6/deno-commons-mod.ts"; + +const COMMONS_LOCAL_ENCRYPT_TINY_ENCRYPT_MASTER_KEY_FILE = getHomeDirOrDie() + + "/.cache/commons-local-encrypt-tiny-encrypt-master-key"; + +interface TinyEncryptSimpleDecryptObject { + code: number; + result: string; +} + +async function loadMasterKey(): Promise { + const masterKeyContent = Deno.readTextFileSync( + COMMONS_LOCAL_ENCRYPT_TINY_ENCRYPT_MASTER_KEY_FILE, + ); + const command = new Deno.Command("tiny-encrypt", { + args: ["simple-decrypt", "--value", masterKeyContent], + }); + const { code, stdout, stderr } = command.outputSync(); + if (code !== 0) { + console.error(`Execute command tiny-encrypt simple-decrypt failed: +code: ${code} +stdout: ${new TextDecoder().decode(stdout)} +stderr: ${new TextDecoder().decode(stderr)}`); + throw new Error(`Decrypt master key failed, code: ${code}`); + } + const tinyEncryptSimpleDecryptObject = JSON.parse( + new TextDecoder().decode(stdout), + ) as TinyEncryptSimpleDecryptObject; + if (tinyEncryptSimpleDecryptObject.code !== 0) { + throw new Error( + `Decrypt master key failed, response code: ${tinyEncryptSimpleDecryptObject.code}`, + ); + } + return hexStringToUint8Array(tinyEncryptSimpleDecryptObject.result); +} + +async function main() { + // TODO ... + console.log(uint8ArrayToHexString(await loadMasterKey())); +} +await main();