Spaces:
Running
Running
File size: 5,192 Bytes
5c2ed06 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
/**
* Config loader
* Pokemon Showdown - http://pokemonshowdown.com/
*
* @license MIT
*/
import * as defaults from '../config/config-example';
import type { GroupInfo, EffectiveGroupSymbol } from './user-groups';
import { ProcessManager, FS } from '../lib';
export type ConfigType = typeof defaults & {
groups: { [symbol: string]: GroupInfo },
groupsranking: EffectiveGroupSymbol[],
greatergroupscache: { [combo: string]: GroupSymbol },
[k: string]: any,
};
/** Map<process flag, config settings for it to turn on> */
const FLAG_PRESETS = new Map([
['--no-security', ['nothrottle', 'noguestsecurity', 'noipchecks']],
]);
const CONFIG_PATH = FS('./config/config.js').path;
export function load(invalidate = false) {
if (invalidate) delete require.cache[CONFIG_PATH];
const config = ({ ...defaults, ...require(CONFIG_PATH) }) as ConfigType;
// config.routes is nested - we need to ensure values are set for its keys as well.
config.routes = { ...defaults.routes, ...config.routes };
// Automatically stop startup if better-sqlite3 isn't installed and SQLite is enabled
if (config.usesqlite) {
try {
require('better-sqlite3');
} catch {
throw new Error(`better-sqlite3 is not installed or could not be loaded, but Config.usesqlite is enabled.`);
}
}
for (const [preset, values] of FLAG_PRESETS) {
if (process.argv.includes(preset)) {
for (const value of values) config[value] = true;
}
}
cacheGroupData(config);
return config;
}
export function cacheGroupData(config: ConfigType) {
if (config.groups) {
// Support for old config groups format.
// Should be removed soon.
reportError(
`You are using a deprecated version of user group specification in config.\n` +
`Support for this will be removed soon.\n` +
`Please ensure that you update your config.js to the new format (see config-example.js, line 457).\n`
);
} else {
config.punishgroups = Object.create(null);
config.groups = Object.create(null);
config.groupsranking = [];
config.greatergroupscache = Object.create(null);
}
const groups = config.groups;
const punishgroups = config.punishgroups;
const cachedGroups: { [k: string]: 'processing' | true } = {};
function isPermission(key: string) {
return !['symbol', 'id', 'name', 'rank', 'globalGroupInPersonalRoom'].includes(key);
}
function cacheGroup(symbol: string, groupData: GroupInfo) {
if (cachedGroups[symbol] === 'processing') {
throw new Error(`Cyclic inheritance in group config for symbol "${symbol}"`);
}
if (cachedGroups[symbol] === true) return;
for (const key in groupData) {
if (isPermission(key)) {
const jurisdiction = groupData[key as 'jurisdiction'];
if (typeof jurisdiction === 'string' && jurisdiction.includes('s')) {
reportError(`Outdated jurisdiction for permission "${key}" of group "${symbol}": 's' is no longer a supported jurisdiction; we now use 'ipself' and 'altsself'`);
delete groupData[key as 'jurisdiction'];
}
}
}
if (groupData['inherit']) {
cachedGroups[symbol] = 'processing';
const inheritGroup = groups[groupData['inherit']];
cacheGroup(groupData['inherit'], inheritGroup);
// Add lower group permissions to higher ranked groups,
// preserving permissions specifically declared for the higher group.
for (const key in inheritGroup) {
if (key in groupData) continue;
if (!isPermission(key)) continue;
(groupData as any)[key] = (inheritGroup as any)[key];
}
delete groupData['inherit'];
}
cachedGroups[symbol] = true;
}
if (config.grouplist) { // Using new groups format.
const grouplist = config.grouplist as any;
const numGroups = grouplist.length;
for (let i = 0; i < numGroups; i++) {
const groupData = grouplist[i];
// punish groups
if (groupData.punishgroup) {
punishgroups[groupData.id] = groupData;
continue;
}
groupData.rank = numGroups - i - 1;
groups[groupData.symbol] = groupData;
config.groupsranking.unshift(groupData.symbol);
}
}
for (const sym in groups) {
const groupData = groups[sym];
cacheGroup(sym, groupData);
}
// hardcode default punishgroups.
if (!punishgroups.locked) {
punishgroups.locked = {
name: 'Locked',
id: 'locked',
symbol: '\u203d',
};
}
if (!punishgroups.muted) {
punishgroups.muted = {
name: 'Muted',
id: 'muted',
symbol: '!',
};
}
}
export function checkRipgrepAvailability() {
if (Config.ripgrepmodlog === undefined) {
const cwd = FS.ROOT_PATH;
Config.ripgrepmodlog = (async () => {
try {
await ProcessManager.exec(['rg', '--version'], { cwd });
await ProcessManager.exec(['tac', '--version'], { cwd });
return true;
} catch {
return false;
}
})();
}
return Config.ripgrepmodlog;
}
function reportError(msg: string) {
// This module generally loads before Monitor, so we put this in a setImmediate to wait for it to load.
// Most child processes don't have Monitor.error, but the main process should always have them, and Config
// errors should always be the same across processes, so this is a neat way to avoid unnecessary logging.
setImmediate(() => global.Monitor?.error?.(msg));
}
export const Config = load();
|