Files
pezkuwi-ui/packages/ui-keyring/src/stores/File.ts
T
Satoshi Qazi Muhammed 6cf7cc635a Update copyright headers to 2026
The header rule in @pezkuwi/dev requires the current year. Year range only; the third-party beachball LICENSE is left as it is.
2026-10-08 11:10:19 +04:00

95 lines
2.3 KiB
TypeScript

// Copyright 2017-2026 @pezkuwi/ui-keyring authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { KeyringJson, KeyringStore } from '../types.js';
import { mkdirpSync } from 'mkdirp';
import fs from 'node:fs';
import path from 'node:path';
// NOTE untested and unused by any known apps, probably broken in various mysterious ways
export class FileStore implements KeyringStore {
#path: string;
constructor (path: string) {
if (!fs.existsSync(path)) {
mkdirpSync(path);
}
this.#path = path;
}
private validateKey (key: string): boolean {
// Make sure the key has a .json extension
if (!key.endsWith('.json')) {
console.error('Non-JSON file requested: ', key);
return false;
}
// Remove '.json'
const keyWithoutExtension = key.slice(0, -5);
// Only allow alphanumeric characters, hyphens, and underscores in the base filename
const safeKeyRegex = /^[a-zA-Z0-9_-]+$/;
if (!safeKeyRegex.test(keyWithoutExtension)) {
console.error('Invalid key format detected: ', key);
return false;
}
return true;
}
public all (fn: (key: string, value: KeyringJson) => void): void {
fs
.readdirSync(this.#path)
.filter((key): boolean => !['.', '..', '.DS_Store'].includes(key))
.forEach((key): void => {
const value = this._readKey(key);
value?.address && fn(key, value);
});
}
public get (key: string, fn: (value: KeyringJson) => void): void {
const value = this._readKey(key);
if (!value?.address) {
throw new Error(`Invalid JSON found for ${key}`);
}
fn(value);
}
public remove (key: string, fn?: () => void): void {
fs.unlinkSync(this._getPath(key));
fn && fn();
}
public set (key: string, value: KeyringJson, fn?: () => void): void {
fs.writeFileSync(this._getPath(key), Buffer.from(JSON.stringify(value), 'utf-8'));
fn && fn();
}
private _getPath (key: string): string {
if (!this.validateKey(key)) {
throw new Error('Invalid key format');
}
return path.join(this.#path, key);
}
private _readKey (key: string): KeyringJson | undefined {
try {
return JSON.parse(
fs.readFileSync(this._getPath(key)).toString('utf-8')
) as KeyringJson;
} catch (error) {
console.error(error);
}
return undefined;
}
}