implemented snapshot name parsing
This commit is contained in:
parent
a024d93e02
commit
85d63d5ae2
8 changed files with 175 additions and 36 deletions
2
.l10nignore
Normal file
2
.l10nignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
js/
|
||||||
|
vendor/
|
0
l10n/.gitkeep
Normal file
0
l10n/.gitkeep
Normal file
|
@ -5,16 +5,30 @@ namespace OCA\GroupfolderFilesystemSnapshots\Entity;
|
||||||
use JsonSerializable;
|
use JsonSerializable;
|
||||||
|
|
||||||
class Snapshot implements JsonSerializable {
|
class Snapshot implements JsonSerializable {
|
||||||
/** @var string */
|
public function __construct(
|
||||||
private $id;
|
private string $id,
|
||||||
|
private string $name,
|
||||||
public function __construct(string $id) {
|
private ?\DateTimeImmutable $createdTimestamp = null,
|
||||||
$this->id = $id;
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getId(): string {
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string {
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreatedTimestamp(): ?\DateTimeImmutable {
|
||||||
|
return $this->createdTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): mixed {
|
public function jsonSerialize(): mixed {
|
||||||
return [
|
return [
|
||||||
'id' => $this->id
|
'id' => $this->id,
|
||||||
|
'name' => $this->name,
|
||||||
|
'createdTimestamp' => $this->createdTimestamp?->getTimestamp(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,12 +70,12 @@ class PathManager {
|
||||||
private function checkIfGroupfolderExists(int $groupfolderId): bool {
|
private function checkIfGroupfolderExists(int $groupfolderId): bool {
|
||||||
$storageId = $this->getRootFolderStorageId();
|
$storageId = $this->getRootFolderStorageId();
|
||||||
if ($storageId === null) {
|
if ($storageId === null) {
|
||||||
return "storage Id null";
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$folder = $this->groupfolderFolderManager->getFolder($groupfolderId, $storageId);
|
$folder = $this->groupfolderFolderManager->getFolder($groupfolderId, $storageId);
|
||||||
if ($folder === false) {
|
if ($folder === false) {
|
||||||
return "Folder does not exist";
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -2,16 +2,21 @@
|
||||||
|
|
||||||
namespace OCA\GroupfolderFilesystemSnapshots\Manager;
|
namespace OCA\GroupfolderFilesystemSnapshots\Manager;
|
||||||
|
|
||||||
use OCA\GroupfolderFilesystemSnapshots\Manager\PathManager;
|
use OCP\IL10N;
|
||||||
|
|
||||||
|
use OCA\GroupfolderFilesystemSnapshots\Manager\PathManager;
|
||||||
|
use OCA\GroupfolderFilesystemSnapshots\Service\SettingsService;
|
||||||
use OCA\GroupfolderFilesystemSnapshots\Entity\Snapshot;
|
use OCA\GroupfolderFilesystemSnapshots\Entity\Snapshot;
|
||||||
|
|
||||||
class SnapshotManager {
|
class SnapshotManager {
|
||||||
private PathManager $pathManager;
|
private string $snapshotNamingScheme = "";
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
public function __construct(PathManager $pathManager){
|
protected readonly IL10N $l10n,
|
||||||
$this->pathManager = $pathManager;
|
private readonly PathManager $pathManager,
|
||||||
|
private readonly SettingsService $settingsService,
|
||||||
|
){
|
||||||
|
$this->snapshotNamingScheme = $this->settingsService->getAppValue("snapshot_naming_scheme");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function validSnapshotId(string $snapshotId) {
|
private function validSnapshotId(string $snapshotId) {
|
||||||
|
@ -27,20 +32,94 @@ class SnapshotManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function createSnapshotEntity(string $id): Snapshot {
|
||||||
|
if ($this->snapshotNamingScheme === "zfs-auto-snapshot" && str_starts_with($id, "zfs-auto-snap")) {
|
||||||
|
if (str_starts_with($id, "zfs-auto-snap_hourly-")) {
|
||||||
|
$name = $this->l10n->t("Automated hourly backup");
|
||||||
|
$datetimestring = str_replace("zfs-auto-snap_hourly-", "", $id);
|
||||||
|
} elseif (str_starts_with($id, "zfs-auto-snap_daily-")) {
|
||||||
|
$name = $this->l10n->t("Automated daily backup");
|
||||||
|
$datetimestring = str_replace("zfs-auto-snap_daily-", "", $id);
|
||||||
|
} elseif (str_starts_with($id, "zfs-auto-snap_weekly-")) {
|
||||||
|
$name = $this->l10n->t("Automated weekly backup");
|
||||||
|
$datetimestring = str_replace("zfs-auto-snap_weekly-", "", $id);
|
||||||
|
} elseif (str_starts_with($id, "zfs-auto-snap_monthly-")) {
|
||||||
|
$name = $this->l10n->t("Automated monthly backup");
|
||||||
|
$datetimestring = str_replace("zfs-auto-snap_monthly-", "", $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($datetimestring)) {
|
||||||
|
$datetimearray = explode("-", $datetimestring);
|
||||||
|
$timestring = array_pop($datetimearray);
|
||||||
|
|
||||||
|
$year = (int)$datetimearray[0];
|
||||||
|
$month = (int)$datetimearray[1];
|
||||||
|
$day = (int)$datetimearray[2];
|
||||||
|
$hour = (int)substr($timestring, 0, 2);
|
||||||
|
$minute = (int)substr($timestring, 2, 2);
|
||||||
|
|
||||||
|
$createdTimestamp = (new \DateTimeImmutable())
|
||||||
|
->setDate($year, $month, $day)
|
||||||
|
->setTime($hour, $minute);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Snapshot(
|
||||||
|
id: $id,
|
||||||
|
name: $name ?: $id,
|
||||||
|
createdTimestamp: $createdTimestamp,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return new Snapshot(
|
||||||
|
id: $id,
|
||||||
|
name: $id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function get(string $snapshotId) {
|
function get(string $snapshotId) {
|
||||||
if(self::snapshotExists($snapshotId)) {
|
if(self::snapshotExists($snapshotId)) {
|
||||||
return new Snapshot($snapshotId);
|
return $this->createSnapshotEntity($snapshotId);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAll() {
|
function getAll(): array {
|
||||||
|
$snapshots = [];
|
||||||
|
|
||||||
$iterator = new \FilesystemIterator($this->pathManager->getFilesystemSnapshotsPath());
|
$iterator = new \FilesystemIterator($this->pathManager->getFilesystemSnapshotsPath());
|
||||||
foreach ($iterator as $fileinfo) {
|
|
||||||
if(!$fileinfo->isDir()) continue;
|
foreach ($iterator as $fileinfo) {
|
||||||
yield new Snapshot($fileinfo->getFilename());
|
if(!$fileinfo->isDir()) continue;
|
||||||
}
|
$snapshots[] = $this->createSnapshotEntity($fileinfo->getFilename());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $snapshots;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAllGenerator(){
|
||||||
|
$iterator = new \FilesystemIterator($this->pathManager->getFilesystemSnapshotsPath());
|
||||||
|
|
||||||
|
foreach ($iterator as $fileinfo) {
|
||||||
|
if(!$fileinfo->isDir()) continue;
|
||||||
|
yield $this->createSnapshotEntity($fileinfo->getFilename());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var $subPathFilter Only return snapshots that have this subfolder in the specified groupfolder
|
||||||
|
*/
|
||||||
|
function getFilteredGenerator(int $groupfolderId, string $subDirectoryFilter) {
|
||||||
|
$iterator = new \FilesystemIterator($this->pathManager->getFilesystemSnapshotsPath());
|
||||||
|
|
||||||
|
$groupfolderSubdirectoryPath = $this->pathManager->getGroupFolderDirectory($groupfolderId, $subDirectoryFilter);
|
||||||
|
|
||||||
|
foreach ($iterator as $fileinfo) {
|
||||||
|
if(!$fileinfo->isDir()) continue;
|
||||||
|
$snapshotId = $fileinfo->getFilename();
|
||||||
|
$filterFullPath = $this->pathManager->convertToSnapshotPath($groupfolderSubdirectoryPath, $snapshotId);
|
||||||
|
if(!(is_dir($filterFullPath))) continue;
|
||||||
|
yield $this->createSnapshotEntity($snapshotId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -58,7 +58,7 @@ class DiffTaskResultService {
|
||||||
$diffTaskResult = $this->find($id);
|
$diffTaskResult = $this->find($id);
|
||||||
|
|
||||||
if($diffTaskResult->getReverted()) {
|
if($diffTaskResult->getReverted()) {
|
||||||
throw new AlreadyRevertedException;
|
throw new AlreadyRevertedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$taskId = $diffTaskResult->getTaskId();
|
$taskId = $diffTaskResult->getTaskId();
|
||||||
|
@ -66,35 +66,35 @@ class DiffTaskResultService {
|
||||||
|
|
||||||
$snapshotPath = $this->pathManager->getGroupFolderSnapshotDirectory($diffTask->getGroupfolderId(), $diffTask->getRelativePath(), $diffTask->getSnapshotId());
|
$snapshotPath = $this->pathManager->getGroupFolderSnapshotDirectory($diffTask->getGroupfolderId(), $diffTask->getRelativePath(), $diffTask->getSnapshotId());
|
||||||
|
|
||||||
$gruenerFolder = $this->pathManager->getGroupfolderMountById($diffTask->getGroupfolderId())->get($diffTask->getRelativePath());
|
$parentFolder = $this->pathManager->getGroupfolderMountById($diffTask->getGroupfolderId())->get($diffTask->getRelativePath());
|
||||||
|
|
||||||
switch($diffTaskResult->getType()) {
|
switch($diffTaskResult->getType()) {
|
||||||
case "CREATION":
|
case "CREATION":
|
||||||
$currentFile = $this->getSubfolderMustExist($gruenerFolder, $diffTaskResult->getCurrentPath());
|
$currentFile = $this->getSubfolderMustExist($parentFolder, $diffTaskResult->getCurrentPath());
|
||||||
$currentFile->delete();
|
$currentFile->delete();
|
||||||
break;
|
break;
|
||||||
case "RENAME":
|
case "RENAME":
|
||||||
$currentFile = $this->getSubfolderMustExist($gruenerFolder, $diffTaskResult->getCurrentPath());
|
$currentFile = $this->getSubfolderMustExist($parentFolder, $diffTaskResult->getCurrentPath());
|
||||||
$beforeDirectory = $this->getOrCreateSubdirectoryByFilepath($gruenerFolder, $diffTaskResult->getBeforePath());
|
$beforeDirectory = $this->getOrCreateSubdirectoryByFilepath($parentFolder, $diffTaskResult->getBeforePath());
|
||||||
|
|
||||||
$currentFile->move($beforeDirectory->getPath() . DIRECTORY_SEPARATOR . basename($diffTaskResult->getBeforePath()));
|
$currentFile->move($beforeDirectory->getPath() . DIRECTORY_SEPARATOR . basename($diffTaskResult->getBeforePath()));
|
||||||
break;
|
break;
|
||||||
case "DELETION":
|
case "DELETION":
|
||||||
$beforeFileFilesystemPath = $snapshotPath . DIRECTORY_SEPARATOR . $diffTaskResult->getBeforePath();
|
$beforeFileFilesystemPath = $snapshotPath . DIRECTORY_SEPARATOR . $diffTaskResult->getBeforePath();
|
||||||
|
|
||||||
$beforeDirectory = $this->getOrCreateSubdirectoryByFilepath($gruenerFolder, $diffTaskResult->getBeforePath());
|
$beforeDirectory = $this->getOrCreateSubdirectoryByFilepath($parentFolder, $diffTaskResult->getBeforePath());
|
||||||
$restoredFile = $beforeDirectory->newFile(basename($diffTaskResult->getBeforePath()));
|
$restoredFile = $beforeDirectory->newFile(basename($diffTaskResult->getBeforePath()));
|
||||||
|
|
||||||
$this->copyFilesystemFileToNextcloudFile($beforeFileFilesystemPath, $restoredFile);
|
$this->copyFilesystemFileToNextcloudFile($beforeFileFilesystemPath, $restoredFile);
|
||||||
break;
|
break;
|
||||||
case "EDIT":
|
case "EDIT":
|
||||||
$currentFile = $this->getSubfolderMustExist($gruenerFolder, $diffTaskResult->getCurrentPath());
|
$currentFile = $this->getSubfolderMustExist($parentFolder, $diffTaskResult->getCurrentPath());
|
||||||
$beforeFileFilesystemPath = $snapshotPath . DIRECTORY_SEPARATOR . $diffTaskResult->getBeforePath();
|
$beforeFileFilesystemPath = $snapshotPath . DIRECTORY_SEPARATOR . $diffTaskResult->getBeforePath();
|
||||||
|
|
||||||
$this->copyFilesystemFileToNextcloudFile($beforeFileFilesystemPath, $currentFile);
|
$this->copyFilesystemFileToNextcloudFile($beforeFileFilesystemPath, $currentFile);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new \Exception;
|
throw new Exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->mapper->markReverted($id);
|
return $this->mapper->markReverted($id);
|
||||||
|
@ -104,7 +104,7 @@ class DiffTaskResultService {
|
||||||
if($parent->nodeExists($path)) {
|
if($parent->nodeExists($path)) {
|
||||||
return $parent->get($path);
|
return $parent->get($path);
|
||||||
} else {
|
} else {
|
||||||
throw new ChangesMadeSinceDiffException;
|
throw new ChangesMadeSinceDiffException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ class DiffTaskResultService {
|
||||||
if($temp instanceof \OCP\Files\Folder) {
|
if($temp instanceof \OCP\Files\Folder) {
|
||||||
$beforeDirectory = $temp;
|
$beforeDirectory = $temp;
|
||||||
} else {
|
} else {
|
||||||
throw new ChangesMadeSinceDiffException;
|
throw new ChangesMadeSinceDiffException();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$beforeDirectory = $beforeDirectory->newFolder($subdir);
|
$beforeDirectory = $beforeDirectory->newFolder($subdir);
|
||||||
|
|
|
@ -9,7 +9,7 @@ use OCP\IConfig;
|
||||||
|
|
||||||
class SettingsService {
|
class SettingsService {
|
||||||
|
|
||||||
private static array $VALID_APP_SETTINGS = ["filesystem_mountpoint_path", "filesystem_snapshots_path"];
|
private static array $VALID_APP_SETTINGS = ["filesystem_mountpoint_path", "filesystem_snapshots_path", "snapshot_naming_scheme"];
|
||||||
|
|
||||||
public function __construct(private IConfig $config) {
|
public function __construct(private IConfig $config) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,13 @@
|
||||||
name="Groupfolder Filesystem Snapshots"
|
name="Groupfolder Filesystem Snapshots"
|
||||||
:limit-width="false">
|
:limit-width="false">
|
||||||
<div v-if="!loading">
|
<div v-if="!loading">
|
||||||
<Field :is="setting.sensitive ? NcPasswordField : NcTextField"
|
<Field :is="getAppSettingComponent(setting)"
|
||||||
v-for="setting in app_settings"
|
v-for="setting in app_settings"
|
||||||
|
v-bind="getAppSettingProps(setting)"
|
||||||
:key="setting.id"
|
:key="setting.id"
|
||||||
class="settings_field"
|
class="settings_field"
|
||||||
:value="settings?.[setting.id]"
|
:value="settings?.[setting.id]"
|
||||||
:label="setting.name"
|
@update:modelValue="(newValue) => updateSetting(setting.id, newValue)"
|
||||||
@update:value="(newValue) => updateSetting(setting.id, newValue)" />
|
@update:value="(newValue) => updateSetting(setting.id, newValue)" />
|
||||||
</div>
|
</div>
|
||||||
</NcSettingsSection>
|
</NcSettingsSection>
|
||||||
|
@ -18,7 +19,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import debounceFunction from 'debounce-fn';
|
import debounceFunction from 'debounce-fn';
|
||||||
import { NcSettingsSection, NcTextField, NcPasswordField } from "@nextcloud/vue"
|
import { NcSettingsSection, NcTextField, NcPasswordField, NcSelect } from "@nextcloud/vue"
|
||||||
|
|
||||||
import { adminSettingsApi } from "./adminSettingsApi.js";
|
import { adminSettingsApi } from "./adminSettingsApi.js";
|
||||||
|
|
||||||
|
@ -26,10 +27,53 @@ let loading = ref(true);
|
||||||
let settings = ref({});
|
let settings = ref({});
|
||||||
|
|
||||||
const app_settings = [
|
const app_settings = [
|
||||||
{id: "filesystem_mountpoint_path", name: "Filesystem Mountpoint Path"},
|
{id: "filesystem_mountpoint_path", name: "Filesystem Mountpoint Path", type: "text" },
|
||||||
{id: "filesystem_snapshots_path", name: "Filesystem Snapshots Path"},
|
{id: "filesystem_snapshots_path", name: "Filesystem Snapshots Path", type: "text"},
|
||||||
|
{
|
||||||
|
id: "snapshot_naming_scheme",
|
||||||
|
name: "Snapshot Naming Scheme",
|
||||||
|
type: "select",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
label: 'None',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'zfs-auto-snapshot',
|
||||||
|
label: 'zfs-auto-snapshot',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const getAppSettingComponent = function(appSetting) {
|
||||||
|
if(appSetting.type === "select") {
|
||||||
|
return NcSelect;
|
||||||
|
} else {
|
||||||
|
if(appSetting?.sensitive) {
|
||||||
|
return NcPasswordField;
|
||||||
|
} else {
|
||||||
|
return NcTextField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAppSettingProps = function(appSetting) {
|
||||||
|
if(appSetting.type === "select") {
|
||||||
|
return {
|
||||||
|
options: appSetting.options,
|
||||||
|
label: "label",
|
||||||
|
inputLabel: appSetting.name,
|
||||||
|
reduce: (option) => ( option.id )
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
label: appSetting.name,
|
||||||
|
clearable: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
adminSettingsApi.getAllSettings().then((result) => {
|
adminSettingsApi.getAllSettings().then((result) => {
|
||||||
settings.value = result;
|
settings.value = result;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue