core: schema: hide secret value

This commit is contained in:
undefined committed 2025-03-12 17:54:32 +08:00
1 parent af118e6ed9
commit caa4ddc414
4 files changed
+85 -22

No files matched your search

+33 -3
View File
@@ -184,23 +184,53 @@ class SystemConfigHandler extends SystemHandler {
async get() {
this.response.template = 'manage_config.html';
let value = this.ctx.config.configSource;
const processNode = (node: any, schema: Schema<any, any>, parent?: any, accessKey?: string) => {
if (['union', 'intersect'].includes(schema.type)) {
for (const item of schema.list) processNode(node, item, parent, accessKey);
}
if (parent && (schema.meta.secret === true || schema.meta.role === 'secret')) {
if (schema.type === 'string') parent[accessKey] = '[hidden]';
// TODO support more types
}
if (schema.type === 'object') {
for (const key in schema.dict) processNode(node[key], schema.dict[key], node, key);
}
};
try {
value = yaml.dump(Schema.intersect(this.ctx.config.settings)(yaml.load(value)));
value = Schema.intersect(this.ctx.config.settings)(yaml.load(value));
for (const schema of this.ctx.config.settings) processNode(value, schema);
} catch (e) { }
this.response.body = {
schema: Schema.intersect(this.ctx.config.settings).toJSON(),
value,
value: yaml.dump(value),
};
}
@requireSudo
@param('value', Types.String)
async post({ }, value: string) {
const oldConfig = yaml.load(this.ctx.config.configSource);
let config;
const processNode = (node: any, old: any, schema: Schema<any, any>, parent?: any, accessKey?: string) => {
if (['union', 'intersect'].includes(schema.type)) {
for (const item of schema.list) processNode(node, old, item, parent, accessKey);
}
if (parent && (schema.meta.secret === true || schema.meta.role === 'secret')) {
if (node === '[hidden]') parent[accessKey] = old[accessKey];
// TODO support more types
}
if (schema.type === 'object') {
for (const key in schema.dict) processNode(node[key] || {}, old[key] || {}, schema.dict[key], node, key);
}
};
try {
config = yaml.load(value);
for (const schema of this.ctx.config.settings) processNode(config, oldConfig, schema, null, '');
} catch (e) {
throw new ValidationError('value');
throw new ValidationError('value', '', e.message);
}
await this.ctx.config.saveConfig(config);
}
+2 -1
View File
@@ -74,7 +74,8 @@ export const Setting = (
declare global {
namespace Schemastery {
interface Meta<T> { // eslint-disable-line @typescript-eslint/no-unused-vars
family: string;
family?: string;
secret?: boolean;
}
}
}
+3 -2
View File
@@ -40,7 +40,7 @@ export class ConfigService extends Service {
applyDelta(source: any, key: string, value: any) {
const path = key.split('.');
if (path.filter((i) => ['__proto__', 'prototype', 'constructor'].includes(i)).length) return false;
if (path.some((i) => ['__proto__', 'prototype', 'constructor'].includes(i))) return false;
const t = path.pop();
const root = JSON.parse(JSON.stringify(source));
let cursor = root;
@@ -96,11 +96,12 @@ export class ConfigService extends Service {
}
const that = this;
const getAccess = (path: (string | symbol)[]) => {
if (path.some((p) => ['__proto__', 'prototype', 'constructor'].includes(String(p)))) throw new Error('Invalid path');
let currentValue = curValue;
for (const p of path) {
currentValue = currentValue[p];
}
if (typeof currentValue !== 'object') return curValue;
if (typeof currentValue !== 'object' || !currentValue) return currentValue;
return new Proxy(currentValue, {
get(self, key: string) {
return getAccess(path.concat(key));