Skip to content

Commit

Permalink
Merge branch 'v5' into v5-catagory-change
Browse files Browse the repository at this point in the history
  • Loading branch information
CKY- authored Dec 25, 2024
2 parents 9c0d144 + dc87e1c commit e101ec3
Show file tree
Hide file tree
Showing 98 changed files with 1,376 additions and 3,163 deletions.
155 changes: 83 additions & 72 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 5 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@
"@aws-sdk/client-polly": "^3.26.0",
"@crowbartools/firebot-custom-scripts-types": "^5.53.2-6",
"@seald-io/nedb": "^4.0.4",
"@twurple/api": "^7.1.0",
"@twurple/auth": "^7.1.0",
"@twurple/chat": "^7.1.0",
"@twurple/eventsub-ws": "^7.1.0",
"@twurple/pubsub": "^7.1.0",
"@twurple/api": "^7.2.1",
"@twurple/auth": "^7.2.1",
"@twurple/chat": "^7.2.1",
"@twurple/eventsub-ws": "^7.2.1",
"@twurple/pubsub": "^7.2.1",
"@types/tinycolor2": "^1.4.6",
"@zunderscore/elgato-light-control": "^1.1.2",
"angular": "^1.8.0",
Expand Down Expand Up @@ -97,7 +97,6 @@
"fuse.js": "^7.0.0",
"glob": "^10.3.10",
"he": "^1.2.0",
"howler": "https://github.com/ebiggz/howler.js/tarball/master",
"list.js": "^1.5.0",
"luxon": "^3.1.1",
"marked": "^13.0.3",
Expand Down
11 changes: 5 additions & 6 deletions src/backend/app-management/electron/events/when-ready.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,9 @@ exports.whenReady = async () => {
const { loadRestrictions } = require("../../../restrictions/builtin-restrictions-loader");
loadRestrictions();

const fontManager = require("../../../fontManager");
fontManager.generateAppFontCssFile();
windowManagement.updateSplashScreenStatus("Loading fonts...");
const { FontManager } = require("../../../font-manager");
await FontManager.loadInstalledFonts();

windowManagement.updateSplashScreenStatus("Loading events...");
const eventsAccess = require("../../../events/events-access");
Expand Down Expand Up @@ -184,8 +185,6 @@ exports.whenReady = async () => {

// get importer in memory
windowManagement.updateSplashScreenStatus("Loading importers...");
const v4Importer = require("../../../import/v4/v4-importer");
v4Importer.setupListeners();

const setupImporter = require("../../../import/setups/setup-importer");
setupImporter.setupListeners();
Expand Down Expand Up @@ -233,8 +232,8 @@ exports.whenReady = async () => {
global.SCRIPTS_DIR = profileManager.getPathInProfile("/scripts/");

windowManagement.updateSplashScreenStatus("Running daily backup...");
const backupManager = require("../../../backup-manager");
await backupManager.onceADayBackUpCheck();
const { BackupManager } = require("../../../backup-manager");
await BackupManager.onceADayBackUpCheck();

// start the REST api server
windowManagement.updateSplashScreenStatus("Starting internal web server...");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ exports.windowsAllClosed = async () => {
logger.debug("All windows closed triggered");

const { SettingsManager } = require("../../../common/settings-manager");
const backupManager = require("../../../backup-manager");
const { BackupManager } = require("../../../backup-manager");

// Stop all scheduled tasks
const scheduledTaskManager = require("../../../timers/scheduled-task-manager");
Expand Down Expand Up @@ -38,7 +38,7 @@ exports.windowsAllClosed = async () => {

if (SettingsManager.getSetting("BackupOnExit")) {
// Make a backup
await backupManager.startBackup(false, app.quit);
await BackupManager.startBackup(false, app.quit);
} else {
app.quit();
}
Expand Down
7 changes: 3 additions & 4 deletions src/backend/app-management/electron/window-management.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const { setupTitlebar, attachTitlebarToWindow } = require("custom-electron-title
const screenHelpers = require("./screen-helpers");
const frontendCommunicator = require("../../common/frontend-communicator");
const { SettingsManager } = require("../../common/settings-manager");
const { BackupManager } = require("../../backup-manager");

const argv = require('../../common/argv-parser');

Expand Down Expand Up @@ -253,9 +254,7 @@ async function createMainWindow() {
toolTip: "Open the folder where backups are stored",
sublabel: "Open the folder where backups are stored",
click: () => {
const backupFolder = path.resolve(
dataAccess.getPathInUserData("/backups/")
);
const backupFolder = BackupManager.backupFolderPath;
shell.openPath(backupFolder);
},
icon: await createIconImage("../../../gui/images/icons/mdi/folder-refresh-outline.png")
Expand Down Expand Up @@ -330,7 +329,7 @@ async function createMainWindow() {
toolTip: "Restores Firebot from a backup",
sublabel: "Restores Firebot from a backup",
click: async () => {
frontendCommunicator.send("restore-backup");
frontendCommunicator.send("backups:start-restore-backup");
},
icon: await createIconImage("../../../gui/images/icons/mdi/backup-restore.png")
},
Expand Down
4 changes: 2 additions & 2 deletions src/backend/auth/twitch-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios from "axios";
import authManager from "./auth-manager";
import accountAccess, { FirebotAccount } from "../common/account-access";
import logger from "../logwrapper";
import { secrets } from "../secrets-manager";
import { SecretsManager } from "../secrets-manager";
import { AuthProviderDefinition } from "./auth";
import { getExpiryDateOfAccessToken } from "@twurple/auth";

Expand All @@ -14,7 +14,7 @@ class TwitchAuthProviders {
readonly streamerAccountProviderId = "twitch:streamer-account";
readonly botAccountProviderId = "twitch:bot-account";

readonly twitchClientId = secrets.twitchClientId;
readonly twitchClientId = SecretsManager.secrets.twitchClientId;

readonly streamerAccountProvider: AuthProviderDefinition = {
id: this.streamerAccountProviderId,
Expand Down
157 changes: 146 additions & 11 deletions src/backend/backup-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,130 @@ const RESTORE_FOLDER_PATH = dataAccess.getPathInTmpDir("/restore");
const PROFILES_FOLDER_PATH = dataAccess.getPathInUserData("/profiles");
const USER_DATA_FOLDER_PATH = dataAccess.getPathInUserData("/");

export type FirebotBackup = {
name: string;
path: string;
backupDate: Date;
version: string;
size: number;
isManual: boolean;
neverDelete: boolean;
};

class BackupManager {
private readonly _backupFolderPath = path.join(dataAccess.getPathInUserData("/"), "backups");
private _backupFolderPath: string = undefined;

constructor() {
frontendCommunicator.on("start-backup", (manualActivation: boolean) => {
this.updateBackupFolderPath();

SettingsManager.on("settings:setting-updated:BackupLocation", () => {
this.updateBackupFolderPath();
});

frontendCommunicator.on("backups:get-backup-folder-path", () => {
return this._backupFolderPath;
});

frontendCommunicator.onAsync("backups:get-backup-list", async () => {
return await this.getBackupList();
});

frontendCommunicator.on("backups:start-backup", (manualActivation: boolean) => {
this.startBackup(manualActivation, () => {
logger.info("backup complete");
frontendCommunicator.send("backup-complete", manualActivation);
frontendCommunicator.send("backups:backup-complete", manualActivation);
});
});

frontendCommunicator.onAsync("restore-backup", async (backupFilePath: string): Promise<{ success: boolean; reason?: string; }> => {
frontendCommunicator.onAsync("backups:restore-backup", async (backupFilePath: string): Promise<{ success: boolean; reason?: string; }> => {
return await this.restoreBackup(backupFilePath);
});

frontendCommunicator.onAsync("backups:delete-backup", async (backupFilePath: string): Promise<boolean> => {
try {
await fsp.unlink(backupFilePath);
return true;
} catch (error) {
logger.error("Error deleting backup", error);
return false;
}
});

frontendCommunicator.onAsync("backups:toggle-backup-prevent-deletion", async (backupFilePath: string) => {
await this.toggleBackupPreventDeletion(backupFilePath);
});

frontendCommunicator.onAsync("backups:move-backup-folder", async (newPath: string) => {
return await this.moveBackupFolder(newPath);
});
}

get backupFolderPath() {
return this._backupFolderPath;
}

async getBackupList(): Promise<FirebotBackup[]> {
const files = await fsp.readdir(this._backupFolderPath);

const backups = await Promise.all(files
.filter(f => f.endsWith(".zip"))
.map(async v => await this.getBackupInfo(path.join(this._backupFolderPath, v))));

backups.sort((a, b) => {
return b.backupDate.getTime() - a.backupDate.getTime();
});

return backups;
}

private async getBackupInfo(backupFilePath: string): Promise<FirebotBackup> {
const fileName = path.basename(backupFilePath);
try {
const fileStats = await fsp.stat(backupFilePath);
const backupDate = fileStats.birthtime;

let version = "Unknown Version";
const versionRegEx = /_(v?\d+\.\d+\.\d+(?:-[a-zA-Z0-9]+(?:\.\d+)?)?)(?:_|\b)/;
const match = fileName.match(versionRegEx);
if (match != null) {
version = match[1];
}

return {
name: fileName.replace(".zip", ""),
path: backupFilePath,
backupDate: backupDate,
version: version,
size: fileStats.size,
isManual: fileName.includes("manual"),
neverDelete: fileName.includes("NODELETE")
};
} catch (error) {
logger.error(`Error reading backup file ${fileName}`, error);
return undefined;
}
}

private updateBackupFolderPath() {
const backupLocation = SettingsManager.getSetting("BackupLocation");
this._backupFolderPath = backupLocation?.length > 0
? backupLocation
: path.join(dataAccess.getPathInUserData("/"), "backups");
}

async toggleBackupPreventDeletion(backupFilePath: string) {
const backup = await this.getBackupInfo(backupFilePath);

backup.neverDelete = !backup.neverDelete;
const oldName = `${backup.name}.zip`;
const newName = backup.neverDelete
? `${backup.name}_NODELETE.zip`
: `${backup.name.replace("_NODELETE", "")}.zip`;

await fsp.rename(
path.join(this._backupFolderPath, oldName),
path.join(this._backupFolderPath, newName)
);
}

private async cleanUpOldBackups(callback: () => void) {
Expand Down Expand Up @@ -106,12 +216,12 @@ class BackupManager {
const folderPath = path.resolve(dataAccess.getPathInUserData("/"));
//archive.directory(folderPath, "profiles");

const varIgnoreInArchive = ['backups/**', 'clips/**', 'logs/**', 'overlay.html'];
const varIgnoreInArchive = ["backups/**", "clips/**", "logs/**", "overlay.html"];
const ignoreResources = SettingsManager.getSetting("BackupIgnoreResources");

if (ignoreResources && !manualActivation) {
logger.info("Ignoring overlay-resources folder");
varIgnoreInArchive.push('overlay-resources/**');
varIgnoreInArchive.push("overlay-resources/**");
}

const fileList = await glob("**/*", {
Expand Down Expand Up @@ -225,12 +335,12 @@ class BackupManager {
const unzippedData = unzipSync(await fsp.readFile(backupFilePath));

for (const [filepath] of Object.entries(unzippedData)) {
if (filepath.includes('profiles')) {
if (filepath.includes("profiles")) {
if (hasGlobalSettings) {
return true;
}
hasProfilesDir = true;
} else if (path.basename(filepath).toLowerCase() === 'global-settings.json') {
} else if (path.basename(filepath).toLowerCase() === "global-settings.json") {
if (hasProfilesDir) {
return true;
}
Expand All @@ -245,7 +355,7 @@ class BackupManager {

const unzippedData = unzipSync(await fsp.readFile(backupFilePath));
for (const [filepath, bytes] of Object.entries(unzippedData)) {
if (filepath.endsWith('/')) {
if (filepath.endsWith("/")) {
continue;
}

Expand All @@ -257,10 +367,35 @@ class BackupManager {

private async copyRestoreFilesToUserData() {
await fsp.cp(RESTORE_FOLDER_PATH, USER_DATA_FOLDER_PATH, { recursive: true, force: true });
logger.info('Copied backup data');
logger.info("Copied backup data");
}

private async moveBackupFolder(newPath: string): Promise<boolean> {
let success = false;

try {
logger.info(`Moving backup files to ${newPath}`);

// Test that we can access the new path
await fsp.access(newPath);

logger.info("Copying old backup files to new location");
await fsp.cp(this._backupFolderPath, newPath, { force: true, recursive: true });

logger.info("Saving new backup location setting");
SettingsManager.saveSetting("BackupLocation", newPath);

logger.info("Backup folder moved successfully");
success = true;
} catch (error) {
logger.error(`Error moving backup folder to ${newPath}`, error);
}

frontendCommunicator.send("backups:move-backup-folder-completed", success);
return success;
}
}

const backupManager = new BackupManager();

export = backupManager;
export { backupManager as BackupManager };
Loading

0 comments on commit e101ec3

Please sign in to comment.