mirror of
https://github.com/unraid/api.git
synced 2026-01-01 06:01:18 -06:00
feat: shared call to createPatch
This commit is contained in:
@@ -3,7 +3,7 @@ import { constants } from 'fs';
|
|||||||
import { access, readFile, unlink, writeFile } from 'fs/promises';
|
import { access, readFile, unlink, writeFile } from 'fs/promises';
|
||||||
import { basename, dirname, join } from 'path';
|
import { basename, dirname, join } from 'path';
|
||||||
|
|
||||||
import { applyPatch, parsePatch, reversePatch } from 'diff';
|
import { applyPatch, createPatch, parsePatch, reversePatch } from 'diff';
|
||||||
|
|
||||||
export interface ShouldApplyWithReason {
|
export interface ShouldApplyWithReason {
|
||||||
shouldApply: boolean;
|
shouldApply: boolean;
|
||||||
@@ -219,4 +219,15 @@ export abstract class FileModification {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async createPatchWithDiff(
|
||||||
|
filePath: string,
|
||||||
|
originalContent: string,
|
||||||
|
newContent: string
|
||||||
|
): Promise<string> {
|
||||||
|
const patch = createPatch(filePath, originalContent, newContent, 'original', 'modified', {
|
||||||
|
context: 5,
|
||||||
|
});
|
||||||
|
return patch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,12 +15,23 @@ export default class AuthRequestModification extends FileModification {
|
|||||||
'/usr/local/emhttp/plugins/dynamix.my.servers/unraid-components/_nuxt/' as const;
|
'/usr/local/emhttp/plugins/dynamix.my.servers/unraid-components/_nuxt/' as const;
|
||||||
id: string = 'auth-request';
|
id: string = 'auth-request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of .js files in the given directory
|
||||||
|
* @param dir - The directory to search for .js files
|
||||||
|
* @returns The list of .js files in the given directory
|
||||||
|
*/
|
||||||
private getJsFiles = async (dir: string) => {
|
private getJsFiles = async (dir: string) => {
|
||||||
const { glob } = await import('glob');
|
const { glob } = await import('glob');
|
||||||
const files = await glob(join(dir, '**/*.js'));
|
const files = await glob(join(dir, '**/*.js'));
|
||||||
const baseDir = '/usr/local/emhttp'; // TODO: Make this configurable
|
const baseDir = '/usr/local/emhttp';
|
||||||
return files.map((file) => (file.startsWith(baseDir) ? file.slice(baseDir.length) : file));
|
return files.map((file) => (file.startsWith(baseDir) ? file.slice(baseDir.length) : file));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a patch for the auth-request.php file
|
||||||
|
* @param overridePath - The path to override the default file path
|
||||||
|
* @returns The patch for the auth-request.php file
|
||||||
|
*/
|
||||||
protected async generatePatch(overridePath?: string): Promise<string> {
|
protected async generatePatch(overridePath?: string): Promise<string> {
|
||||||
const jsFiles = await this.getJsFiles(this.webComponentsDirectory);
|
const jsFiles = await this.getJsFiles(this.webComponentsDirectory);
|
||||||
this.logger.debug(`Found ${jsFiles.length} .js files in ${this.webComponentsDirectory}`);
|
this.logger.debug(`Found ${jsFiles.length} .js files in ${this.webComponentsDirectory}`);
|
||||||
@@ -42,19 +53,7 @@ export default class AuthRequestModification extends FileModification {
|
|||||||
// Create new content by finding the array declaration and adding our files after it
|
// Create new content by finding the array declaration and adding our files after it
|
||||||
const newContent = fileContent.replace(/(\$arrWhitelist\s*=\s*\[)/, `$1\n${filesToAddString}`);
|
const newContent = fileContent.replace(/(\$arrWhitelist\s*=\s*\[)/, `$1\n${filesToAddString}`);
|
||||||
|
|
||||||
// Generate and return patch
|
return this.createPatchWithDiff(overridePath ?? this.filePath, fileContent, newContent);
|
||||||
const patch = createPatch(
|
|
||||||
overridePath ?? this.filePath,
|
|
||||||
fileContent,
|
|
||||||
newContent,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
context: 3,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return patch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async shouldApply(): Promise<ShouldApplyWithReason> {
|
async shouldApply(): Promise<ShouldApplyWithReason> {
|
||||||
|
|||||||
@@ -46,18 +46,7 @@ export default class DefaultPageLayoutModification extends FileModification {
|
|||||||
|
|
||||||
const newContent = this.applyToSource(fileContent);
|
const newContent = this.applyToSource(fileContent);
|
||||||
|
|
||||||
const patch = createPatch(
|
return this.createPatchWithDiff(overridePath ?? this.filePath, fileContent, newContent);
|
||||||
overridePath ?? this.filePath,
|
|
||||||
fileContent,
|
|
||||||
newContent,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
context: 2,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return patch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async shouldApply(): Promise<ShouldApplyWithReason> {
|
async shouldApply(): Promise<ShouldApplyWithReason> {
|
||||||
|
|||||||
@@ -35,21 +35,11 @@ export class LogRotateModification extends FileModification {
|
|||||||
? await readFile(this.filePath, 'utf8')
|
? await readFile(this.filePath, 'utf8')
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const patch = createPatch(
|
return this.createPatchWithDiff(
|
||||||
overridePath ?? this.filePath,
|
overridePath ?? this.filePath,
|
||||||
currentContent,
|
currentContent,
|
||||||
this.logRotateConfig,
|
this.logRotateConfig
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
context: 3,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// After applying patch, ensure file permissions are correct
|
|
||||||
await execa('chown', ['root:root', this.filePath]).catch((err) => this.logger.error(err));
|
|
||||||
|
|
||||||
return patch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async shouldApply(): Promise<ShouldApplyWithReason> {
|
async shouldApply(): Promise<ShouldApplyWithReason> {
|
||||||
|
|||||||
@@ -17,18 +17,7 @@ export default class NotificationsPageModification extends FileModification {
|
|||||||
|
|
||||||
const newContent = NotificationsPageModification.applyToSource(fileContent);
|
const newContent = NotificationsPageModification.applyToSource(fileContent);
|
||||||
|
|
||||||
const patch = createPatch(
|
return this.createPatchWithDiff(overridePath ?? this.filePath, fileContent, newContent);
|
||||||
overridePath ?? this.filePath,
|
|
||||||
fileContent,
|
|
||||||
newContent,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
context: 3,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return patch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async shouldApply(): Promise<ShouldApplyWithReason> {
|
async shouldApply(): Promise<ShouldApplyWithReason> {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
Index: /usr/local/emhttp/auth-request.php
|
Index: /usr/local/emhttp/auth-request.php
|
||||||
===================================================================
|
===================================================================
|
||||||
--- /usr/local/emhttp/auth-request.php
|
--- /usr/local/emhttp/auth-request.php original
|
||||||
+++ /usr/local/emhttp/auth-request.php
|
+++ /usr/local/emhttp/auth-request.php modified
|
||||||
@@ -15,6 +15,7 @@
|
@@ -13,10 +13,11 @@
|
||||||
|
}
|
||||||
|
session_write_close();
|
||||||
}
|
}
|
||||||
|
|
||||||
$arrWhitelist = [
|
$arrWhitelist = [
|
||||||
@@ -10,3 +12,5 @@ Index: /usr/local/emhttp/auth-request.php
|
|||||||
'/webGui/styles/clear-sans-bold-italic.eot',
|
'/webGui/styles/clear-sans-bold-italic.eot',
|
||||||
'/webGui/styles/clear-sans-bold-italic.woff',
|
'/webGui/styles/clear-sans-bold-italic.woff',
|
||||||
'/webGui/styles/clear-sans-bold-italic.ttf',
|
'/webGui/styles/clear-sans-bold-italic.ttf',
|
||||||
|
'/webGui/styles/clear-sans-bold-italic.svg',
|
||||||
|
'/webGui/styles/clear-sans-bold.eot',
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
Index: /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
Index: /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
||||||
===================================================================
|
===================================================================
|
||||||
--- /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
--- /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php original
|
||||||
+++ /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
+++ /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php modified
|
||||||
@@ -557,14 +557,5 @@
|
@@ -554,20 +554,11 @@
|
||||||
|
return 'three';
|
||||||
|
}
|
||||||
|
function openNotifier() {
|
||||||
$.post('/webGui/include/Notify.php',{cmd:'get',csrf_token:csrf_token},function(msg) {
|
$.post('/webGui/include/Notify.php',{cmd:'get',csrf_token:csrf_token},function(msg) {
|
||||||
$.each($.parseJSON(msg), function(i, notify){
|
$.each($.parseJSON(msg), function(i, notify){
|
||||||
- $.jGrowl(notify.subject+'<br>'+notify.description,{
|
- $.jGrowl(notify.subject+'<br>'+notify.description,{
|
||||||
@@ -18,7 +21,13 @@ Index: /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
|||||||
+
|
+
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -698,6 +689,6 @@
|
}
|
||||||
|
function closeNotifier() {
|
||||||
|
$.post('/webGui/include/Notify.php',{cmd:'get',csrf_token:csrf_token},function(msg) {
|
||||||
|
@@ -695,12 +686,12 @@
|
||||||
|
}
|
||||||
|
// create list of nchan scripts to be started
|
||||||
|
if (isset($button['Nchan'])) nchan_merge($button['root'], $button['Nchan']);
|
||||||
}
|
}
|
||||||
|
|
||||||
-echo "<div class='nav-user show'><a id='board' href='#' class='hand'><b id='bell' class='icon-u-bell system'></b></a></div>";
|
-echo "<div class='nav-user show'><a id='board' href='#' class='hand'><b id='bell' class='icon-u-bell system'></b></a></div>";
|
||||||
@@ -26,7 +35,13 @@ Index: /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
|||||||
+
|
+
|
||||||
if ($themes2) echo "</div>";
|
if ($themes2) echo "</div>";
|
||||||
echo "</div></div>";
|
echo "</div></div>";
|
||||||
@@ -904,20 +895,12 @@
|
foreach ($buttons as $button) {
|
||||||
|
annotate($button['file']);
|
||||||
|
// include page specific stylesheets (if existing)
|
||||||
|
@@ -901,26 +892,18 @@
|
||||||
|
case 'warning': bell2++; break;
|
||||||
|
case 'normal' : bell3++; break;
|
||||||
|
}
|
||||||
<?if ($notify['display']==0):?>
|
<?if ($notify['display']==0):?>
|
||||||
if (notify.show) {
|
if (notify.show) {
|
||||||
- $.jGrowl(notify.subject+'<br>'+notify.description,{
|
- $.jGrowl(notify.subject+'<br>'+notify.description,{
|
||||||
@@ -52,7 +67,13 @@ Index: /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
|||||||
+
|
+
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1222,4 +1205,5 @@
|
});
|
||||||
|
|
||||||
|
var nchan_plugins = new NchanSubscriber('/sub/plugins',{subscriber:'websocket'});
|
||||||
|
@@ -1219,7 +1202,8 @@
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
+<unraid-toaster rich-colors close-button position="<?= ($notify['position'] === 'center') ? 'top-center' : $notify['position'] ?>"></unraid-toaster>
|
+<unraid-toaster rich-colors close-button position="<?= ($notify['position'] === 'center') ? 'top-center' : $notify['position'] ?>"></unraid-toaster>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Index: /etc/logrotate.d/unraid-api
|
Index: /etc/logrotate.d/unraid-api
|
||||||
===================================================================
|
===================================================================
|
||||||
--- /etc/logrotate.d/unraid-api
|
--- /etc/logrotate.d/unraid-api original
|
||||||
+++ /etc/logrotate.d/unraid-api
|
+++ /etc/logrotate.d/unraid-api modified
|
||||||
@@ -0,0 +1,12 @@
|
@@ -0,0 +1,12 @@
|
||||||
+
|
+
|
||||||
+/var/log/unraid-api/*.log {
|
+/var/log/unraid-api/*.log {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
Index: /usr/local/emhttp/plugins/dynamix/Notifications.page
|
Index: /usr/local/emhttp/plugins/dynamix/Notifications.page
|
||||||
===================================================================
|
===================================================================
|
||||||
--- /usr/local/emhttp/plugins/dynamix/Notifications.page
|
--- /usr/local/emhttp/plugins/dynamix/Notifications.page original
|
||||||
+++ /usr/local/emhttp/plugins/dynamix/Notifications.page
|
+++ /usr/local/emhttp/plugins/dynamix/Notifications.page modified
|
||||||
@@ -135,23 +135,7 @@
|
@@ -133,27 +133,11 @@
|
||||||
|
_(Auto-close)_ (_(seconds)_):
|
||||||
|
: <input type="number" name="life" class="a" min="0" max="60" value="<?=$notify['life']?>"> _(a value of zero means no automatic closure)_
|
||||||
|
|
||||||
:notifications_auto_close_help:
|
:notifications_auto_close_help:
|
||||||
|
|
||||||
@@ -26,3 +28,5 @@ Index: /usr/local/emhttp/plugins/dynamix/Notifications.page
|
|||||||
_(Store notifications to flash)_:
|
_(Store notifications to flash)_:
|
||||||
: <select name="path" class="a">
|
: <select name="path" class="a">
|
||||||
<?=mk_option($notify['path'], "/tmp/notifications", _("No"))?>
|
<?=mk_option($notify['path'], "/tmp/notifications", _("No"))?>
|
||||||
|
<?=mk_option($notify['path'], "/boot/config/plugins/dynamix/notifications", _("Yes"))?>
|
||||||
|
</select>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Index: /usr/local/emhttp/plugins/dynamix/include/.login.php
|
|||||||
===================================================================
|
===================================================================
|
||||||
--- /usr/local/emhttp/plugins/dynamix/include/.login.php original
|
--- /usr/local/emhttp/plugins/dynamix/include/.login.php original
|
||||||
+++ /usr/local/emhttp/plugins/dynamix/include/.login.php modified
|
+++ /usr/local/emhttp/plugins/dynamix/include/.login.php modified
|
||||||
@@ -1,5 +1,33 @@
|
@@ -1,6 +1,34 @@
|
||||||
<?php
|
<?php
|
||||||
+
|
+
|
||||||
+
|
+
|
||||||
@@ -36,7 +36,9 @@ Index: /usr/local/emhttp/plugins/dynamix/include/.login.php
|
|||||||
|
|
||||||
// Only start a session to check if they have a cookie that looks like our session
|
// Only start a session to check if they have a cookie that looks like our session
|
||||||
$server_name = strtok($_SERVER['HTTP_HOST'],":");
|
$server_name = strtok($_SERVER['HTTP_HOST'],":");
|
||||||
@@ -203,9 +231,9 @@
|
if (!empty($_COOKIE['unraid_'.md5($server_name)])) {
|
||||||
|
@@ -202,11 +230,11 @@
|
||||||
|
if ($failCount == $maxFails) my_logger("Ignoring login attempts for {$username} from {$remote_addr}");
|
||||||
throw new Exception(_('Too many invalid login attempts'));
|
throw new Exception(_('Too many invalid login attempts'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +49,9 @@ Index: /usr/local/emhttp/plugins/dynamix/include/.login.php
|
|||||||
// Bail if we need a token but it's invalid
|
// Bail if we need a token but it's invalid
|
||||||
if (isWildcardCert() && $twoFactorRequired && !verifyTwoFactorToken($username, $token)) throw new Exception(_('Invalid 2FA token'));
|
if (isWildcardCert() && $twoFactorRequired && !verifyTwoFactorToken($username, $token)) throw new Exception(_('Invalid 2FA token'));
|
||||||
|
|
||||||
@@ -537,8 +565,9 @@
|
// Successful login, start session
|
||||||
|
@@ -536,10 +564,11 @@
|
||||||
|
document.body.textContent = '';
|
||||||
document.body.appendChild(errorElement);
|
document.body.appendChild(errorElement);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -57,3 +61,4 @@ Index: /usr/local/emhttp/plugins/dynamix/include/.login.php
|
|||||||
<? if (($twoFactorRequired && !empty($token)) || !$twoFactorRequired) { ?>
|
<? if (($twoFactorRequired && !empty($token)) || !$twoFactorRequired) { ?>
|
||||||
<div class="js-addTimeout hidden">
|
<div class="js-addTimeout hidden">
|
||||||
<p class="error" style="padding-top:10px;"><?=_('Transparent 2FA Token timed out')?></p>
|
<p class="error" style="padding-top:10px;"><?=_('Transparent 2FA Token timed out')?></p>
|
||||||
|
<a href="https://forums.unraid.net/my-servers/" class="button button--small" title="<?=_('Go to My Servers Dashboard')?>"><?=_('Go to My Servers Dashboard')?></a>
|
||||||
|
|||||||
@@ -64,14 +64,7 @@ function verifyUsernamePasswordAndSSO(string $username, string $password): bool
|
|||||||
newContent = newContent.replace(/<\/form>/i, `</form>\n${tagToInject}`);
|
newContent = newContent.replace(/<\/form>/i, `</form>\n${tagToInject}`);
|
||||||
|
|
||||||
// Create and return the patch
|
// Create and return the patch
|
||||||
const patch = createPatch(
|
return this.createPatchWithDiff(overridePath ?? this.filePath, originalContent, newContent);
|
||||||
overridePath ?? this.filePath,
|
|
||||||
originalContent,
|
|
||||||
newContent,
|
|
||||||
'original',
|
|
||||||
'modified'
|
|
||||||
);
|
|
||||||
return patch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async shouldApply(): Promise<ShouldApplyWithReason> {
|
async shouldApply(): Promise<ShouldApplyWithReason> {
|
||||||
|
|||||||
Reference in New Issue
Block a user