mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 21:00:41 +00:00
0a001bebcc
- Introduced a new DomainRulesPage component for managing custom and global equivalent domains. - Updated AppMainRoutes to include a route for domain rules. - Added API functions to fetch and save domain rules. - Enhanced localization with new strings for domain rules in multiple languages. - Updated styles for the new domain rules interface and ensured responsiveness. - Added types for domain rules in the TypeScript definitions.
153 lines
4.5 KiB
JavaScript
153 lines
4.5 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
|
|
const DEFAULT_REF = 'main';
|
|
const OUTPUT_DIR = path.join(process.cwd(), 'src', 'static');
|
|
const OUT_FILE = path.join(OUTPUT_DIR, 'global_domains.bitwarden.json');
|
|
const META_FILE = path.join(OUTPUT_DIR, 'global_domains.bitwarden.meta.json');
|
|
const ENUM_PATH = 'src/Core/Enums/GlobalEquivalentDomainsType.cs';
|
|
const STATIC_STORE_PATH = 'src/Core/Utilities/StaticStore.cs';
|
|
|
|
function parseArgs(argv) {
|
|
const args = { ref: process.env.BITWARDEN_SERVER_REF || DEFAULT_REF };
|
|
for (let i = 0; i < argv.length; i += 1) {
|
|
const arg = argv[i];
|
|
if (arg === '--ref' && argv[i + 1]) {
|
|
args.ref = argv[i + 1];
|
|
i += 1;
|
|
} else if (arg.startsWith('--ref=')) {
|
|
args.ref = arg.slice('--ref='.length);
|
|
}
|
|
}
|
|
return args;
|
|
}
|
|
|
|
function rawUrl(ref, filePath) {
|
|
return `https://raw.githubusercontent.com/bitwarden/server/${encodeURIComponent(ref)}/${filePath}`;
|
|
}
|
|
|
|
async function fetchText(url) {
|
|
const response = await fetch(url, {
|
|
headers: {
|
|
'User-Agent': 'NodeWarden global domains sync',
|
|
Accept: 'text/plain',
|
|
},
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch ${url}: HTTP ${response.status}`);
|
|
}
|
|
return response.text();
|
|
}
|
|
|
|
function parseEnumTypes(source) {
|
|
const map = new Map();
|
|
const enumMatch = source.match(/enum\s+GlobalEquivalentDomainsType\b[\s\S]*?\{([\s\S]*?)\}/);
|
|
if (!enumMatch) {
|
|
throw new Error('GlobalEquivalentDomainsType enum was not found');
|
|
}
|
|
|
|
const body = enumMatch[1].replace(/\/\/.*$/gm, '');
|
|
const entryRe = /\b([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(\d+)\b/g;
|
|
let match;
|
|
while ((match = entryRe.exec(body)) !== null) {
|
|
map.set(match[1], Number(match[2]));
|
|
}
|
|
|
|
if (!map.size) {
|
|
throw new Error('No enum values were parsed from GlobalEquivalentDomainsType');
|
|
}
|
|
return map;
|
|
}
|
|
|
|
function parseStringList(source) {
|
|
const domains = [];
|
|
const stringRe = /"((?:\\.|[^"\\])*)"/g;
|
|
let match;
|
|
while ((match = stringRe.exec(source)) !== null) {
|
|
domains.push(match[1].replace(/\\"/g, '"').trim().toLowerCase());
|
|
}
|
|
return Array.from(new Set(domains.filter(Boolean)));
|
|
}
|
|
|
|
function parseGlobalDomains(source, enumTypes) {
|
|
const out = [];
|
|
const addRe = /GlobalDomains\.Add\s*\(\s*GlobalEquivalentDomainsType\.([A-Za-z_][A-Za-z0-9_]*)\s*,\s*new\s+List(?:<\s*string\s*>)?\s*\{([\s\S]*?)\}\s*\)\s*;/g;
|
|
let match;
|
|
while ((match = addRe.exec(source)) !== null) {
|
|
const name = match[1];
|
|
const type = enumTypes.get(name);
|
|
if (!Number.isInteger(type)) {
|
|
throw new Error(`GlobalDomains references unknown enum value ${name}`);
|
|
}
|
|
|
|
const domains = parseStringList(match[2]);
|
|
if (domains.length < 2) {
|
|
throw new Error(`GlobalDomains.${name} has fewer than two domains`);
|
|
}
|
|
|
|
out.push({
|
|
type,
|
|
domains,
|
|
excluded: false,
|
|
});
|
|
}
|
|
|
|
if (!out.length) {
|
|
throw new Error('No GlobalDomains.Add(...) rules were parsed from StaticStore.cs');
|
|
}
|
|
return out;
|
|
}
|
|
|
|
const { ref } = parseArgs(process.argv.slice(2));
|
|
const enumUrl = rawUrl(ref, ENUM_PATH);
|
|
const staticStoreUrl = rawUrl(ref, STATIC_STORE_PATH);
|
|
|
|
const [enumSource, staticStoreSource] = await Promise.all([
|
|
fetchText(enumUrl),
|
|
fetchText(staticStoreUrl),
|
|
]);
|
|
|
|
const enumTypes = parseEnumTypes(enumSource);
|
|
const rules = parseGlobalDomains(staticStoreSource, enumTypes);
|
|
const domainsCount = rules.reduce((sum, rule) => sum + rule.domains.length, 0);
|
|
const rulesJson = JSON.stringify(rules, null, 2);
|
|
|
|
async function readJsonFile(filePath) {
|
|
try {
|
|
return JSON.parse(await readFile(filePath, 'utf8'));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
const existingRules = await readJsonFile(OUT_FILE);
|
|
const existingMeta = await readJsonFile(META_FILE);
|
|
const unchangedRules = JSON.stringify(existingRules) === JSON.stringify(rules);
|
|
const unchangedRef = existingMeta?.ref === ref;
|
|
|
|
const meta = {
|
|
source: 'https://github.com/bitwarden/server',
|
|
ref,
|
|
generatedAt: unchangedRules && unchangedRef && existingMeta?.generatedAt
|
|
? existingMeta.generatedAt
|
|
: new Date().toISOString(),
|
|
rulesCount: rules.length,
|
|
domainsCount,
|
|
sourceFiles: [
|
|
ENUM_PATH,
|
|
STATIC_STORE_PATH,
|
|
],
|
|
sourceUrls: [
|
|
enumUrl,
|
|
staticStoreUrl,
|
|
],
|
|
};
|
|
|
|
await mkdir(OUTPUT_DIR, { recursive: true });
|
|
await writeFile(OUT_FILE, `${rulesJson}\n`, 'utf8');
|
|
await writeFile(META_FILE, `${JSON.stringify(meta, null, 2)}\n`, 'utf8');
|
|
|
|
console.log(`Wrote ${rules.length} global domain rules (${domainsCount} domains) from bitwarden/server@${ref}.`);
|