mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-03-21 18:04:10 -05:00
Add command to list node-persist data
This commit is contained in:
parent
65b36a384e
commit
bf48cc201b
138
src/cli/users.ts
138
src/cli/users.ts
|
|
@ -1,16 +1,26 @@
|
|||
import createDebug from 'debug';
|
||||
import * as persist from 'node-persist';
|
||||
import Table from './util/table.js';
|
||||
import type { Arguments as ParentArguments } from '../cli.js';
|
||||
import { Argv } from '../util/yargs.js';
|
||||
import { initStorage } from '../util/storage.js';
|
||||
import { initStorage, iterateLocalStorage } from '../util/storage.js';
|
||||
import { SavedToken } from '../common/auth/coral.js';
|
||||
import { SavedMoonToken } from '../common/auth/moon.js';
|
||||
import { Jwt } from '../util/jwt.js';
|
||||
import { NintendoAccountSessionTokenJwtPayload } from '../api/na.js';
|
||||
|
||||
const debug = createDebug('cli:users');
|
||||
const debugRemove = createDebug('cli:users:remove');
|
||||
debugRemove.enabled = true;
|
||||
|
||||
export const command = 'users <command>';
|
||||
export const desc = 'Manage authenticated Nintendo Accounts';
|
||||
|
||||
interface AppSavedMonitorState {
|
||||
users: {id: string;}[];
|
||||
discord_presence: {source: {na_id: string;};} | null;
|
||||
}
|
||||
|
||||
export function builder(yargs: Argv<ParentArguments>) {
|
||||
return yargs.command('list', 'Lists known Nintendo Accounts', () => {}, async argv => {
|
||||
const storage = await initStorage(argv.dataPath);
|
||||
|
|
@ -86,37 +96,107 @@ export function builder(yargs: Argv<ParentArguments>) {
|
|||
}, async argv => {
|
||||
const storage = await initStorage(argv.dataPath);
|
||||
|
||||
const selected: string | undefined = await storage.getItem('SelectedUser');
|
||||
const nsoToken: string | undefined = await storage.getItem('NintendoAccountToken.' + argv.user);
|
||||
const nsoCache: SavedToken | undefined = nsoToken ? await storage.getItem('NsoToken.' + nsoToken) : undefined;
|
||||
const moonToken: string | undefined = await storage.getItem('NintendoAccountToken-pctl.' + argv.user);
|
||||
|
||||
if (!nsoToken && !moonToken) {
|
||||
throw new Error('Unknown user');
|
||||
}
|
||||
await removeUserData(storage, argv.user);
|
||||
|
||||
if (selected === argv.user) {
|
||||
await storage.removeItem('SelectedUser');
|
||||
await storage.removeItem('SessionToken');
|
||||
}
|
||||
|
||||
await storage.removeItem('IksmToken.' + nsoToken);
|
||||
await storage.removeItem('NookToken.' + nsoToken);
|
||||
await storage.removeItem('NookUsers.' + nsoToken);
|
||||
|
||||
for (const key of await storage.keys()) {
|
||||
if (key.startsWith('NookAuthToken.' + nsoToken + '.')) await storage.removeItem(key);
|
||||
if (nsoCache && key.startsWith('WebServicePersistentData.' + nsoCache.nsoAccount.user.nsaId + '.'))
|
||||
await storage.removeItem(key);
|
||||
}
|
||||
|
||||
await storage.removeItem('NintendoAccountToken.' + argv.user);
|
||||
await storage.removeItem('NsoToken.' + nsoToken);
|
||||
await storage.removeItem('NintendoAccountToken-pctl.' + argv.user);
|
||||
await storage.removeItem('MoonToken.' + moonToken);
|
||||
|
||||
const users = new Set(await storage.getItem('NintendoAccountIds') ?? []);
|
||||
users.delete(argv.user);
|
||||
await storage.setItem('NintendoAccountIds', [...users]);
|
||||
console.log('Removed %s%s from storage', argv.user,
|
||||
nsoCache ? ' (' + nsoCache.user.nickname + '/' + nsoCache.nsoAccount.user.name + ')' : '');
|
||||
console.log('Additional cached data about this user may still exist in nxapi\'s storage. Use `nxapi util storage list` to list all data stored with node-persist.');
|
||||
});
|
||||
}
|
||||
|
||||
async function removeUserData(
|
||||
storage: persist.LocalStorage, na_id: string,
|
||||
only?: ('coral' | 'moon')[], only_cached = false
|
||||
) {
|
||||
let coral_saved_token: SavedToken | undefined = undefined;
|
||||
|
||||
for await (const {key, value} of iterateLocalStorage(storage)) {
|
||||
if (key.startsWith('NsoToken.') && (!only || only.includes('coral'))) {
|
||||
const session_token = key.substr(9);
|
||||
const [jwt, sig] = Jwt.decode<NintendoAccountSessionTokenJwtPayload>(session_token);
|
||||
if (jwt.payload.sub !== na_id) continue;
|
||||
|
||||
if (!coral_saved_token) coral_saved_token = value;
|
||||
|
||||
debugRemove('Removing data for coral session token', session_token);
|
||||
await removeSavedCoralTokenData(storage, session_token);
|
||||
}
|
||||
|
||||
if (key.startsWith('MoonToken.') && (!only || only.includes('moon'))) {
|
||||
const session_token = key.substr(9);
|
||||
const [jwt, sig] = Jwt.decode<NintendoAccountSessionTokenJwtPayload>(session_token);
|
||||
if (jwt.payload.sub !== na_id) continue;
|
||||
|
||||
debugRemove('Removing data for moon session token', session_token);
|
||||
await removeSavedMoonTokenData(storage, session_token);
|
||||
}
|
||||
}
|
||||
|
||||
if (coral_saved_token && (!only || only.includes('coral'))) {
|
||||
for await (const {key} of iterateLocalStorage(storage)) {
|
||||
if (key.startsWith('WebServicePersistentData.' + coral_saved_token.nsoAccount.user.nsaId + '.')) {
|
||||
debugRemove('Removing web service persisted data', key.substr(25));
|
||||
await storage.removeItem(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const selected: string | undefined = await storage.getItem('SelectedUser');
|
||||
let coral_session_token: string | undefined = await storage.getItem('NintendoAccountToken.' + na_id);
|
||||
let moon_session_token: string | undefined = await storage.getItem('NintendoAccountToken-pctl.' + na_id);
|
||||
|
||||
if (coral_session_token && (!only || only.includes('coral')) && !only_cached) {
|
||||
debugRemove('Removing coral session token');
|
||||
await storage.removeItem('NintendoAccountToken.' + na_id);
|
||||
|
||||
const app_monitors: AppSavedMonitorState | undefined = await storage.getItem('AppMonitors');
|
||||
if (app_monitors) {
|
||||
app_monitors.users = app_monitors.users.filter(u => u.id !== na_id);
|
||||
if (app_monitors.discord_presence?.source.na_id === na_id) app_monitors.discord_presence = null;
|
||||
await storage.setItem('AppMonitors', app_monitors);
|
||||
}
|
||||
|
||||
coral_session_token = undefined;
|
||||
}
|
||||
|
||||
if (moon_session_token && (!only || only.includes('moon')) && !only_cached) {
|
||||
debugRemove('Removing moon session token');
|
||||
await storage.removeItem('NintendoAccountToken-pctl.' + na_id);
|
||||
moon_session_token = undefined;
|
||||
}
|
||||
|
||||
if (!coral_session_token && !moon_session_token && selected === na_id) {
|
||||
debugRemove('Deselecting user');
|
||||
await storage.removeItem('SelectedUser');
|
||||
await storage.removeItem('SessionToken');
|
||||
}
|
||||
|
||||
const users = new Set(await storage.getItem('NintendoAccountIds') ?? []);
|
||||
if (!coral_session_token && !moon_session_token && users.has(na_id)) {
|
||||
debugRemove('Removing user from list');
|
||||
users.delete(na_id);
|
||||
await storage.setItem('NintendoAccountIds', [...users]);
|
||||
}
|
||||
}
|
||||
|
||||
async function removeSavedCoralTokenData(storage: persist.LocalStorage, session_token: string) {
|
||||
await storage.removeItem('SessionToken');
|
||||
|
||||
await storage.removeItem('IksmToken.' + session_token);
|
||||
await storage.removeItem('NookToken.' + session_token);
|
||||
await storage.removeItem('NookUsers.' + session_token);
|
||||
await storage.removeItem('BulletToken.' + session_token);
|
||||
|
||||
for await (const {key, value} of iterateLocalStorage(storage)) {
|
||||
if (key.startsWith('NookAuthToken.' + session_token + '.')) await storage.removeItem(key);
|
||||
|
||||
if (value === session_token) await storage.removeItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
async function removeSavedMoonTokenData(storage: persist.LocalStorage, session_token: string) {
|
||||
await storage.removeItem('MoonToken.' + session_token);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ export * as exportDiscordTitles from './export-discord-titles.js';
|
|||
export * as discordActivity from './discord-activity.js';
|
||||
export * as discordRpc from './discord-rpc.js';
|
||||
export * as remoteConfig from './remote-config.js';
|
||||
export * as storage from './storage.js';
|
||||
|
|
|
|||
43
src/cli/util/storage.ts
Normal file
43
src/cli/util/storage.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import * as util from 'node:util';
|
||||
import createDebug from 'debug';
|
||||
import type { Arguments as ParentArguments } from '../util.js';
|
||||
import { Argv } from '../../util/yargs.js';
|
||||
import { initStorage, iterateLocalStorage } from '../../util/storage.js';
|
||||
import Table from './table.js';
|
||||
import { createHash } from 'node:crypto';
|
||||
|
||||
const debug = createDebug('cli:util:storage');
|
||||
|
||||
export const command = 'storage';
|
||||
export const desc = 'Manage node-persist data';
|
||||
|
||||
export function builder(yargs: Argv<ParentArguments>) {
|
||||
return yargs.demandCommand().command('list', 'List all object', yargs => {}, async argv => {
|
||||
const storage = await initStorage(argv.dataPath);
|
||||
|
||||
const table = new Table({
|
||||
head: [
|
||||
'File',
|
||||
'Key',
|
||||
'Value',
|
||||
],
|
||||
colWidths: [10, 42, 80],
|
||||
});
|
||||
|
||||
for await (const data of iterateLocalStorage(storage)) {
|
||||
const value = util.inspect(data.value, {
|
||||
compact: true,
|
||||
});
|
||||
|
||||
table.push([
|
||||
createHash('md5').update(data.key).digest('hex'),
|
||||
data.key.length > 40 ? data.key.substr(0, 37) + '...' : data.key,
|
||||
value.length > 200 ? value.substr(0, 197) + '...' : value,
|
||||
]);
|
||||
}
|
||||
|
||||
table.sort((a, b) => a[1] > b[1] ? 1 : b[1] > a[1] ? -1 : 0);
|
||||
|
||||
console.log(table.toString());
|
||||
});
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import * as path from 'node:path';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import createDebug from 'debug';
|
||||
import persist from 'node-persist';
|
||||
import getPaths from 'env-paths';
|
||||
|
|
@ -15,3 +16,16 @@ export async function initStorage(dir: string) {
|
|||
await storage.init();
|
||||
return storage;
|
||||
}
|
||||
|
||||
export async function* iterateLocalStorage(storage: persist.LocalStorage) {
|
||||
const dir = (storage as unknown as {options: persist.InitOptions}).options.dir!;
|
||||
|
||||
for await (const file of await fs.opendir(dir)) {
|
||||
if (!file.isFile()) continue;
|
||||
|
||||
const datum = await storage.readFile(path.join(dir, file.name)) as persist.Datum;
|
||||
if (!datum || !datum.key) continue;
|
||||
|
||||
yield datum;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user