CLI sqlite migratior
This commit is contained in:
parent
72fabd5346
commit
42e08dec93
@ -5,12 +5,12 @@ import fs from "fs";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
import Auth from "./plugins/auth";
|
import Auth from "./plugins/auth";
|
||||||
import Client, {UserConfig} from "./client";
|
import Client, { UserConfig } from "./client";
|
||||||
import Config from "./config";
|
import Config from "./config";
|
||||||
import {NetworkConfig} from "./models/network";
|
import { NetworkConfig } from "./models/network";
|
||||||
import WebPush from "./plugins/webpush";
|
import WebPush from "./plugins/webpush";
|
||||||
import log from "./log";
|
import log from "./log";
|
||||||
import {Server} from "socket.io";
|
import { Server } from "socket.io";
|
||||||
|
|
||||||
class ClientManager {
|
class ClientManager {
|
||||||
clients: Client[];
|
clients: Client[];
|
||||||
@ -107,17 +107,21 @@ class ClientManager {
|
|||||||
|
|
||||||
// Existing users removed since last time users were loaded
|
// Existing users removed since last time users were loaded
|
||||||
_.difference(loaded, updatedUsers).forEach((name) => {
|
_.difference(loaded, updatedUsers).forEach((name) => {
|
||||||
const client = _.find(this.clients, {name});
|
const client = _.find(this.clients, { name });
|
||||||
|
|
||||||
if (client) {
|
if (client) {
|
||||||
client.quit(true);
|
client.quit(true);
|
||||||
this.clients = _.without(this.clients, client);
|
this.clients = _.without(this.clients, client);
|
||||||
log.info(`User ${colors.bold(name)} disconnected and removed.`);
|
log.info(
|
||||||
|
`User ${colors.bold(
|
||||||
|
name
|
||||||
|
)} disconnected and removed.`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
1000,
|
1000,
|
||||||
{maxWait: 10000}
|
{ maxWait: 10000 }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -197,7 +201,8 @@ class ClientManager {
|
|||||||
if (
|
if (
|
||||||
userFolderStat &&
|
userFolderStat &&
|
||||||
userFileStat &&
|
userFileStat &&
|
||||||
(userFolderStat.uid !== userFileStat.uid || userFolderStat.gid !== userFileStat.gid)
|
(userFolderStat.uid !== userFileStat.uid ||
|
||||||
|
userFolderStat.gid !== userFileStat.gid)
|
||||||
) {
|
) {
|
||||||
log.warn(
|
log.warn(
|
||||||
`User ${colors.green(
|
`User ${colors.green(
|
||||||
@ -227,13 +232,16 @@ class ClientManager {
|
|||||||
networks: client.networks.map((n) => n.export()),
|
networks: client.networks.map((n) => n.export()),
|
||||||
});
|
});
|
||||||
const newUser = JSON.stringify(json, null, "\t");
|
const newUser = JSON.stringify(json, null, "\t");
|
||||||
const newHash = crypto.createHash("sha256").update(newUser).digest("hex");
|
const newHash = crypto
|
||||||
|
.createHash("sha256")
|
||||||
|
.update(newUser)
|
||||||
|
.digest("hex");
|
||||||
|
|
||||||
return {newUser, newHash};
|
return { newUser, newHash };
|
||||||
}
|
}
|
||||||
|
|
||||||
saveUser(client: Client, callback?: (err?: any) => void) {
|
saveUser(client: Client, callback?: (err?: any) => void) {
|
||||||
const {newUser, newHash} = this.getDataToSave(client);
|
const { newUser, newHash } = this.getDataToSave(client);
|
||||||
|
|
||||||
// Do not write to disk if the exported data hasn't actually changed
|
// Do not write to disk if the exported data hasn't actually changed
|
||||||
if (client.fileHash === newHash) {
|
if (client.fileHash === newHash) {
|
||||||
@ -254,7 +262,9 @@ class ClientManager {
|
|||||||
return callback ? callback() : true;
|
return callback ? callback() : true;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
log.error(`Failed to update user ${colors.green(client.name)} (${e})`);
|
log.error(
|
||||||
|
`Failed to update user ${colors.green(client.name)} (${e})`
|
||||||
|
);
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(e);
|
callback(e);
|
||||||
@ -266,7 +276,9 @@ class ClientManager {
|
|||||||
const userPath = Config.getUserConfigPath(name);
|
const userPath = Config.getUserConfigPath(name);
|
||||||
|
|
||||||
if (!fs.existsSync(userPath)) {
|
if (!fs.existsSync(userPath)) {
|
||||||
log.error(`Tried to remove non-existing user ${colors.green(name)}.`);
|
log.error(
|
||||||
|
`Tried to remove non-existing user ${colors.green(name)}.`
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +287,7 @@ class ClientManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readUserConfig(name: string) {
|
readUserConfig(name: string) {
|
||||||
const userPath = Config.getUserConfigPath(name);
|
const userPath = Config.getUserConfigPath(name);
|
||||||
|
|
||||||
if (!fs.existsSync(userPath)) {
|
if (!fs.existsSync(userPath)) {
|
||||||
|
@ -3,7 +3,7 @@ import log from "../log";
|
|||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import colors from "chalk";
|
import colors from "chalk";
|
||||||
import {Command} from "commander";
|
import { Command } from "commander";
|
||||||
import Helper from "../helper";
|
import Helper from "../helper";
|
||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
import Utils from "./utils";
|
import Utils from "./utils";
|
||||||
@ -42,6 +42,7 @@ program.addCommand(require("./install").default);
|
|||||||
program.addCommand(require("./uninstall").default);
|
program.addCommand(require("./uninstall").default);
|
||||||
program.addCommand(require("./upgrade").default);
|
program.addCommand(require("./upgrade").default);
|
||||||
program.addCommand(require("./outdated").default);
|
program.addCommand(require("./outdated").default);
|
||||||
|
program.addCommand(require("./storage").default);
|
||||||
|
|
||||||
if (!Config.values.public) {
|
if (!Config.values.public) {
|
||||||
require("./users").default.forEach((command: Command) => {
|
require("./users").default.forEach((command: Command) => {
|
||||||
@ -64,7 +65,7 @@ function createPackagesFolder() {
|
|||||||
const packagesConfig = path.join(packagesPath, "package.json");
|
const packagesConfig = path.join(packagesPath, "package.json");
|
||||||
|
|
||||||
// Create node_modules folder, otherwise yarn will start walking upwards to find one
|
// Create node_modules folder, otherwise yarn will start walking upwards to find one
|
||||||
fs.mkdirSync(path.join(packagesPath, "node_modules"), {recursive: true});
|
fs.mkdirSync(path.join(packagesPath, "node_modules"), { recursive: true });
|
||||||
|
|
||||||
// Create package.json with private set to true, if it doesn't exist already
|
// Create package.json with private set to true, if it doesn't exist already
|
||||||
if (!fs.existsSync(packagesConfig)) {
|
if (!fs.existsSync(packagesConfig)) {
|
||||||
@ -99,7 +100,9 @@ function verifyFileOwner() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const configStat = fs.statSync(path.join(Config.getHomePath(), "config.js"));
|
const configStat = fs.statSync(
|
||||||
|
path.join(Config.getHomePath(), "config.js")
|
||||||
|
);
|
||||||
|
|
||||||
if (configStat && configStat.uid !== uid) {
|
if (configStat && configStat.uid !== uid) {
|
||||||
log.warn(
|
log.warn(
|
||||||
|
68
server/command-line/storage.ts
Normal file
68
server/command-line/storage.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import log from "../log";
|
||||||
|
import { Command } from "commander";
|
||||||
|
import ClientManager from "../clientManager";
|
||||||
|
import Utils from "./utils";
|
||||||
|
import SqliteMessageStorage from "../plugins/messageStorage/sqlite";
|
||||||
|
|
||||||
|
const program = new Command("storage").description(
|
||||||
|
"various utilities related to the message storage"
|
||||||
|
);
|
||||||
|
|
||||||
|
program
|
||||||
|
.command("migrate")
|
||||||
|
.argument("[user]", "migrate a specific user only, all if not provided")
|
||||||
|
.description("Migrate message storage where needed")
|
||||||
|
.on("--help", Utils.extraHelp)
|
||||||
|
.action(function (user) {
|
||||||
|
runMigrations(user).catch((err) => {
|
||||||
|
log.error(err.toString());
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function runMigrations(user: string) {
|
||||||
|
const manager = new ClientManager();
|
||||||
|
const users = manager.getUsers();
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
if (!users.includes(user)) {
|
||||||
|
throw new Error(`invalid user ${user}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return migrateUser(manager, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const name of users) {
|
||||||
|
await migrateUser(manager, name);
|
||||||
|
// if any migration fails we blow up,
|
||||||
|
// chances are the rest won't complete either
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// runs sqlite migrations for a user, which must exist
|
||||||
|
async function migrateUser(manager: ClientManager, user: string) {
|
||||||
|
log.info("handling user", user);
|
||||||
|
|
||||||
|
if (!isUserLogEnabled(manager, user)) {
|
||||||
|
log.info("logging disabled for user", user, ". Skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sqlite = new SqliteMessageStorage(user);
|
||||||
|
await sqlite.enable(); // enable runs migrations
|
||||||
|
await sqlite.close();
|
||||||
|
log.info("user", user, "migrated successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
function isUserLogEnabled(manager: ClientManager, user: string): boolean {
|
||||||
|
const conf = manager.readUserConfig(user);
|
||||||
|
|
||||||
|
if (!conf) {
|
||||||
|
log.error("Could not open user configuration of", user);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf.log;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default program;
|
Loading…
Reference in New Issue
Block a user