@duplicati/ngclient 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/.dockerignore +13 -0
  2. package/.github/workflows/npm-publish.yml +59 -0
  3. package/.prettierignore +13 -0
  4. package/.prettierrc +12 -0
  5. package/.vscode/extensions.json +4 -0
  6. package/.vscode/launch.json +13 -0
  7. package/.vscode/settings.json +8 -0
  8. package/.vscode/tasks.json +24 -0
  9. package/Dockerfile +38 -0
  10. package/LICENSE +21 -0
  11. package/README.md +27 -0
  12. package/angular.json +98 -0
  13. package/bun.lockb +0 -0
  14. package/openapi-ts.config.ts +13 -0
  15. package/package.json +46 -0
  16. package/scripts/generate-package-json.ts +15 -0
  17. package/src/app/about/about.component.html +15 -0
  18. package/src/app/about/about.component.scss +19 -0
  19. package/src/app/about/about.component.ts +13 -0
  20. package/src/app/about/changelog/changelog.component.html +3 -0
  21. package/src/app/about/changelog/changelog.component.scss +7 -0
  22. package/src/app/about/changelog/changelog.component.ts +18 -0
  23. package/src/app/about/general/general.component.html +9 -0
  24. package/src/app/about/general/general.component.scss +23 -0
  25. package/src/app/about/general/general.component.ts +30 -0
  26. package/src/app/about/libraries/libraries.component.html +3 -0
  27. package/src/app/about/libraries/libraries.component.scss +7 -0
  28. package/src/app/about/libraries/libraries.component.ts +18 -0
  29. package/src/app/about/logs/logs.component.html +5 -0
  30. package/src/app/about/logs/logs.component.scss +7 -0
  31. package/src/app/about/logs/logs.component.ts +18 -0
  32. package/src/app/about/system-info/system-info.component.html +3 -0
  33. package/src/app/about/system-info/system-info.component.scss +7 -0
  34. package/src/app/about/system-info/system-info.component.ts +17 -0
  35. package/src/app/add-backup/add-backup.component.html +29 -0
  36. package/src/app/add-backup/add-backup.component.scss +38 -0
  37. package/src/app/add-backup/add-backup.component.ts +14 -0
  38. package/src/app/app.component.ts +35 -0
  39. package/src/app/app.config.ts +23 -0
  40. package/src/app/app.routes.ts +121 -0
  41. package/src/app/core/components/file-tree/file-tree.component.html +87 -0
  42. package/src/app/core/components/file-tree/file-tree.component.scss +80 -0
  43. package/src/app/core/components/file-tree/file-tree.component.ts +485 -0
  44. package/src/app/core/components/logo/logo.component.html +22 -0
  45. package/src/app/core/components/logo/logo.component.scss +12 -0
  46. package/src/app/core/components/logo/logo.component.ts +11 -0
  47. package/src/app/core/components/status-bar/status-bar.component.html +38 -0
  48. package/src/app/core/components/status-bar/status-bar.component.scss +27 -0
  49. package/src/app/core/components/status-bar/status-bar.component.ts +33 -0
  50. package/src/app/core/components/status-bar/status-bar.state.ts +182 -0
  51. package/src/app/core/components/toggle-card/toggle-card.component.html +11 -0
  52. package/src/app/core/components/toggle-card/toggle-card.component.scss +49 -0
  53. package/src/app/core/components/toggle-card/toggle-card.component.ts +28 -0
  54. package/src/app/core/interceptors/http.interceptor.ts +69 -0
  55. package/src/app/core/interceptors/websocket.interceptor.ts +66 -0
  56. package/src/app/core/openapi/core/ApiError.ts +21 -0
  57. package/src/app/core/openapi/core/ApiRequestOptions.ts +21 -0
  58. package/src/app/core/openapi/core/ApiResult.ts +7 -0
  59. package/src/app/core/openapi/core/OpenAPI.ts +55 -0
  60. package/src/app/core/openapi/core/request.ts +337 -0
  61. package/src/app/core/openapi/index.ts +6 -0
  62. package/src/app/core/openapi/schemas.gen.ts +3611 -0
  63. package/src/app/core/openapi/services.gen.ts +1460 -0
  64. package/src/app/core/openapi/types.gen.ts +1510 -0
  65. package/src/app/core/pipes/duration.pipe.ts +27 -0
  66. package/src/app/core/pipes/relative-time.pipe.ts +14 -0
  67. package/src/app/core/providers/dayjs.ts +16 -0
  68. package/src/app/core/services/dict.en.ts +0 -0
  69. package/src/app/core/services/dictionary.service.ts +12 -0
  70. package/src/app/core/services/localstorage.token.ts +82 -0
  71. package/src/app/core/services/password-generator.service.ts +39 -0
  72. package/src/app/core/services/relay-websocket.service.ts +268 -0
  73. package/src/app/core/services/timespan-literals.service.ts +25 -0
  74. package/src/app/core/states/app-auth.state.ts +87 -0
  75. package/src/app/core/states/backups.state.ts +71 -0
  76. package/src/app/core/states/relayconfig.state.ts +23 -0
  77. package/src/app/core/states/sysinfo.state.ts +31 -0
  78. package/src/app/core/states/tasks.state.ts +34 -0
  79. package/src/app/core/types/subscribed.ts +7 -0
  80. package/src/app/core/validators/custom.validators.ts +120 -0
  81. package/src/app/create-edit-backup/create-edit-backup.component.html +28 -0
  82. package/src/app/create-edit-backup/create-edit-backup.component.scss +15 -0
  83. package/src/app/create-edit-backup/create-edit-backup.component.ts +33 -0
  84. package/src/app/create-edit-backup/create-edit-backup.state.ts +582 -0
  85. package/src/app/create-edit-backup/destination/destination.component.html +367 -0
  86. package/src/app/create-edit-backup/destination/destination.component.scss +85 -0
  87. package/src/app/create-edit-backup/destination/destination.component.ts +249 -0
  88. package/src/app/create-edit-backup/destination/destination.config.ts +103 -0
  89. package/src/app/create-edit-backup/destination/destination.mapper.ts +177 -0
  90. package/src/app/create-edit-backup/general/general.component.html +108 -0
  91. package/src/app/create-edit-backup/general/general.component.scss +34 -0
  92. package/src/app/create-edit-backup/general/general.component.ts +136 -0
  93. package/src/app/create-edit-backup/options/options.component.html +95 -0
  94. package/src/app/create-edit-backup/options/options.component.scss +42 -0
  95. package/src/app/create-edit-backup/options/options.component.ts +113 -0
  96. package/src/app/create-edit-backup/schedule/schedule.component.html +102 -0
  97. package/src/app/create-edit-backup/schedule/schedule.component.scss +38 -0
  98. package/src/app/create-edit-backup/schedule/schedule.component.ts +137 -0
  99. package/src/app/create-edit-backup/source-data/source-data.component.html +93 -0
  100. package/src/app/create-edit-backup/source-data/source-data.component.scss +41 -0
  101. package/src/app/create-edit-backup/source-data/source-data.component.ts +114 -0
  102. package/src/app/home/backup.const.ts +167 -0
  103. package/src/app/home/home.component.html +98 -0
  104. package/src/app/home/home.component.scss +88 -0
  105. package/src/app/home/home.component.ts +54 -0
  106. package/src/app/layout/layout.component.html +64 -0
  107. package/src/app/layout/layout.component.scss +24 -0
  108. package/src/app/layout/layout.component.ts +51 -0
  109. package/src/app/layout/layout.state.ts +70 -0
  110. package/src/app/login/login.component.html +12 -0
  111. package/src/app/login/login.component.scss +25 -0
  112. package/src/app/login/login.component.ts +34 -0
  113. package/src/app/logout/logout.component.html +1 -0
  114. package/src/app/logout/logout.component.scss +3 -0
  115. package/src/app/logout/logout.component.ts +18 -0
  116. package/src/app/restore/restore.component.html +56 -0
  117. package/src/app/restore/restore.component.scss +37 -0
  118. package/src/app/restore/restore.component.ts +55 -0
  119. package/src/app/restore-flow/options/options.component.html +57 -0
  120. package/src/app/restore-flow/options/options.component.scss +16 -0
  121. package/src/app/restore-flow/options/options.component.ts +58 -0
  122. package/src/app/restore-flow/restore-flow.component.html +14 -0
  123. package/src/app/restore-flow/restore-flow.component.scss +15 -0
  124. package/src/app/restore-flow/restore-flow.component.ts +23 -0
  125. package/src/app/restore-flow/restore-flow.state.ts +120 -0
  126. package/src/app/restore-flow/select-files/select-files.component.html +42 -0
  127. package/src/app/restore-flow/select-files/select-files.component.scss +16 -0
  128. package/src/app/restore-flow/select-files/select-files.component.ts +129 -0
  129. package/src/app/settings/settings.component.html +256 -0
  130. package/src/app/settings/settings.component.scss +106 -0
  131. package/src/app/settings/settings.component.ts +475 -0
  132. package/src/assets/duplicati-logo.png +0 -0
  133. package/src/assets/duplicati-logo.svg +10 -0
  134. package/src/assets/favicon.ico +0 -0
  135. package/src/assets/images/backup.png +0 -0
  136. package/src/assets/images/dark-theme.png +0 -0
  137. package/src/assets/images/folder.png +0 -0
  138. package/src/assets/images/light-theme.png +0 -0
  139. package/src/assets/images/new-backup.png +0 -0
  140. package/src/assets/images/restore.png +0 -0
  141. package/src/assets/images/system-theme.png +0 -0
  142. package/src/assets/spk.css +33 -0
  143. package/src/assets/spk.woff2 +0 -0
  144. package/src/environments/environment-token.ts +4 -0
  145. package/src/environments/environment.prod.ts +6 -0
  146. package/src/environments/environment.ts +6 -0
  147. package/src/index.html +15 -0
  148. package/src/main.ts +6 -0
  149. package/src/proxy.conf.mjs +6 -0
  150. package/src/styles.scss +41 -0
  151. package/tsconfig.app.json +15 -0
  152. package/tsconfig.json +30 -0
@@ -0,0 +1,31 @@
1
+ import { computed, inject, Injectable, signal } from '@angular/core';
2
+ import { finalize } from 'rxjs';
3
+ import { DuplicatiServerService, SystemInfoDto } from '../openapi';
4
+
5
+ @Injectable({
6
+ providedIn: 'root',
7
+ })
8
+ export class SysinfoState {
9
+ #dupServer = inject(DuplicatiServerService);
10
+
11
+ isLoaded = signal(false);
12
+ systemInfo = signal<SystemInfoDto | null>(null);
13
+ backendModules = computed(() => {
14
+ return this.systemInfo()?.BackendModules ?? [];
15
+ });
16
+
17
+ preloadSystemInfo(force = false) {
18
+ if (!force && this.systemInfo()) {
19
+ return;
20
+ }
21
+
22
+ this.#dupServer
23
+ .getApiV1Systeminfo()
24
+ .pipe(finalize(() => this.isLoaded.set(true)))
25
+ .subscribe({
26
+ next: (res) => {
27
+ this.systemInfo.set(res);
28
+ },
29
+ });
30
+ }
31
+ }
@@ -0,0 +1,34 @@
1
+ import { inject, Injectable, signal } from '@angular/core';
2
+ import { finalize } from 'rxjs';
3
+ import { TasksService } from '../openapi';
4
+ import { Subscribed } from '../types/subscribed';
5
+
6
+ export type TasksRes = Subscribed<ReturnType<TasksService['getApiV1Tasks']>>;
7
+
8
+ @Injectable({
9
+ providedIn: 'root',
10
+ })
11
+ export class TasksState {
12
+ #taskService = inject(TasksService);
13
+
14
+ tasks = signal<TasksRes>([]);
15
+ tasksLoading = signal(false);
16
+
17
+ getTasks() {
18
+ this.tasksLoading.set(true);
19
+
20
+ this.#taskService
21
+ .getApiV1Tasks()
22
+ .pipe(finalize(() => this.tasksLoading.set(false)))
23
+ .subscribe({
24
+ next: (res) => {
25
+ this.tasks.set(res);
26
+ },
27
+ });
28
+ }
29
+
30
+ getTask(id: number) {
31
+ // TODO maybe get task from server
32
+ return this.tasks().find((x) => x.TaskID === id) ?? null;
33
+ }
34
+ }
@@ -0,0 +1,7 @@
1
+ export type Subscribed<T> = T extends null | undefined
2
+ ? T // special case for `null | undefined` when not in `--strictNullChecks` mode
3
+ : T extends object & { subscribe(onfulfilled: infer F, ...args: infer _): any } // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
4
+ ? F extends (value: infer V, ...args: infer _) => any // if the argument to `then` is callable, extracts the first argument
5
+ ? Subscribed<V> // recursively unwrap the value
6
+ : never // the argument to `then` was not callable
7
+ : T; // non-object or non-thenable
@@ -0,0 +1,120 @@
1
+ import { AbstractControl, FormArray, FormGroup, ValidationErrors, ValidatorFn } from '@angular/forms';
2
+
3
+ export function validateWhen(
4
+ predicate: (parent: FormGroup | FormArray) => boolean,
5
+ validators: ValidatorFn | ValidatorFn[],
6
+ errorNamespace = 'conditional'
7
+ ): ValidatorFn {
8
+ return (control: AbstractControl) => {
9
+ if (!control.parent) {
10
+ return null;
11
+ }
12
+
13
+ let error: ValidationErrors | null = null;
14
+
15
+ if (predicate(control.parent)) {
16
+ const validatorArr = Array.isArray(validators) ? validators : [validators];
17
+
18
+ error = validatorArr.find((validator) => validator(control) !== null) ?? null;
19
+ }
20
+
21
+ if (errorNamespace && error) {
22
+ const customError: ValidationErrors = {};
23
+ customError[errorNamespace] = error;
24
+ error = customError;
25
+ }
26
+
27
+ return error as ValidationErrors | null;
28
+ };
29
+ }
30
+
31
+ export function watchField() {
32
+ return (control: AbstractControl): null => {
33
+ if (!control.parent) {
34
+ return null;
35
+ }
36
+
37
+ Object.values(control.parent.controls).forEach((ctrl) => {
38
+ if (ctrl !== control) {
39
+ ctrl.updateValueAndValidity();
40
+ }
41
+ });
42
+
43
+ return null;
44
+ };
45
+ }
46
+
47
+ export function validateIf<T>(
48
+ conditionalFieldName: string,
49
+ conditionalValue: T | T[],
50
+ validators: ValidatorFn | ValidatorFn[],
51
+ errorNamespace = 'conditional'
52
+ ): ValidatorFn {
53
+ return (control: AbstractControl) => {
54
+ if (!control.parent) {
55
+ return null;
56
+ }
57
+
58
+ let error: ValidationErrors | null = null;
59
+
60
+ const conditionalFormCtrl = control.parent.get(conditionalFieldName);
61
+
62
+ if (!conditionalFormCtrl) {
63
+ console.warn('Conditional field not found');
64
+
65
+ return null;
66
+ }
67
+
68
+ const isConditionalValueArray = Array.isArray(conditionalValue);
69
+
70
+ if (
71
+ isConditionalValueArray
72
+ ? conditionalFormCtrl.value?.includes(conditionalValue)
73
+ : conditionalFormCtrl.value === conditionalValue
74
+ ) {
75
+ const validatorArr = Array.isArray(validators) ? validators : [validators];
76
+
77
+ error = validatorArr.find((validator) => validator(control) !== null) ?? null;
78
+ }
79
+
80
+ if (errorNamespace && error) {
81
+ const customError: ValidationErrors = {};
82
+ customError[errorNamespace] = error;
83
+ error = customError;
84
+ }
85
+
86
+ return error;
87
+ };
88
+ }
89
+
90
+ export function accumulatedMax(conditionalValue: number): ValidatorFn {
91
+ return (control: AbstractControl): ValidationErrors => {
92
+ let error: { accumulatedMax: string } | ValidationErrors = {};
93
+
94
+ const accumulatedControlValue = control.value.reduce((a: number, b: number) => a + b, 0);
95
+
96
+ if (accumulatedControlValue > conditionalValue) {
97
+ error = {
98
+ accumulatedMax: `Total page count exceeds the maximum (${conditionalValue}) count.`,
99
+ };
100
+ }
101
+
102
+ return error;
103
+ };
104
+ }
105
+
106
+ export function accumulatedMin(conditionalValue: number): ValidatorFn {
107
+ return (control: AbstractControl): ValidationErrors => {
108
+ let error: { accumulatedMax: string } | ValidationErrors = {};
109
+
110
+ const accumulatedControlValue = control.value.reduce((a: number, b: number) => a + b, 0);
111
+
112
+ if (accumulatedControlValue < conditionalValue) {
113
+ error = {
114
+ accumulatedMin: `Total page count exceeds the minimum (${conditionalValue}) count.`,
115
+ };
116
+ }
117
+
118
+ return error;
119
+ };
120
+ }
@@ -0,0 +1,28 @@
1
+ <app-status-bar />
2
+
3
+ <spk-stepper>
4
+ <div spk-step routerLinkActive="active" [routerLink]="['/backup', backupId(), 'general']">
5
+ <spk-radio />
6
+ General
7
+ </div>
8
+ <div spk-step routerLinkActive="active" [routerLink]="['/backup', backupId(), 'destination']">
9
+ <spk-radio />
10
+ Destination
11
+ </div>
12
+ <div spk-step routerLinkActive="active" [routerLink]="['/backup', backupId(), 'source-data']">
13
+ <spk-radio />
14
+ Source Data
15
+ </div>
16
+ <div spk-step routerLinkActive="active" [routerLink]="['/backup', backupId(), 'schedule']">
17
+ <spk-radio />
18
+ Schedule
19
+ </div>
20
+ <div spk-step routerLinkActive="active" [routerLink]="['/backup', backupId(), 'options']">
21
+ <spk-radio />
22
+ Options
23
+ </div>
24
+ </spk-stepper>
25
+
26
+ <section>
27
+ <router-outlet></router-outlet>
28
+ </section>
@@ -0,0 +1,15 @@
1
+ @use '@sparkle-ui/core/styles/helpers' as *;
2
+
3
+ :host {
4
+ display: block;
5
+ }
6
+
7
+ spk-stepper {
8
+ max-width: p2r(500);
9
+ margin: p2r(40) auto;
10
+ }
11
+
12
+ section {
13
+ max-width: p2r(500);
14
+ margin: 0 auto p2r(40);
15
+ }
@@ -0,0 +1,33 @@
1
+ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
2
+ import { ActivatedRoute, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
3
+ import { SparkleIconComponent, SparkleRadioComponent, SparkleStepperComponent } from '@sparkle-ui/core';
4
+ import StatusBarComponent from '../core/components/status-bar/status-bar.component';
5
+ import { CreateEditBackupState } from './create-edit-backup.state';
6
+
7
+ @Component({
8
+ selector: 'app-create-edit-backup',
9
+ standalone: true,
10
+ imports: [
11
+ RouterOutlet,
12
+ RouterLink,
13
+ RouterLinkActive,
14
+ StatusBarComponent,
15
+ SparkleRadioComponent,
16
+ SparkleStepperComponent,
17
+ SparkleIconComponent,
18
+ ],
19
+ templateUrl: './create-edit-backup.component.html',
20
+ styleUrl: './create-edit-backup.component.scss',
21
+ changeDetection: ChangeDetectionStrategy.OnPush,
22
+ providers: [CreateEditBackupState],
23
+ })
24
+ export default class CreateEditBackupComponent {
25
+ #route = inject(ActivatedRoute);
26
+ #createEditBackupState = inject(CreateEditBackupState);
27
+
28
+ backupId = this.#createEditBackupState.backupId;
29
+
30
+ ngOnInit() {
31
+ this.#createEditBackupState.init(this.#route.snapshot.params['id']);
32
+ }
33
+ }