@duplicati/ngclient 0.0.1 → 0.0.5

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 (197) hide show
  1. package/chunk-2DCEAXBG.js +1 -0
  2. package/chunk-2WYEYW3N.js +1 -0
  3. package/chunk-43IMNGQL.js +1 -0
  4. package/chunk-63RKHEAK.js +1 -0
  5. package/chunk-6HCEXB2Z.js +1 -0
  6. package/chunk-7POJIDMZ.js +1 -0
  7. package/chunk-7PZ2UDVE.js +1 -0
  8. package/chunk-AYLGFKRR.js +1 -0
  9. package/chunk-BFEHLVAY.js +1 -0
  10. package/chunk-BHHWIMLQ.js +1 -0
  11. package/chunk-BOT7P6RM.js +1 -0
  12. package/chunk-BRNQESTL.js +1 -0
  13. package/chunk-EIZWLO2R.js +2 -0
  14. package/chunk-EJZT4TGZ.js +1 -0
  15. package/chunk-F3A4UEZK.js +1 -0
  16. package/chunk-GAT3R5MA.js +2 -0
  17. package/chunk-GBVLF3BS.js +1 -0
  18. package/chunk-GY2VPOAT.js +1 -0
  19. package/chunk-HHIFLECY.js +1 -0
  20. package/chunk-IMLDBH2T.js +1 -0
  21. package/chunk-IOM7KF2W.js +1 -0
  22. package/chunk-J6KTHSHX.js +1 -0
  23. package/chunk-JRXXG6O7.js +1 -0
  24. package/chunk-LD5EWOQM.js +1 -0
  25. package/chunk-LT6J7X37.js +1 -0
  26. package/chunk-OP5BV4PG.js +1 -0
  27. package/chunk-OX435KV6.js +1 -0
  28. package/chunk-PJNE2N54.js +1 -0
  29. package/chunk-QTQWYR6H.js +1 -0
  30. package/chunk-RCES7NCR.js +1 -0
  31. package/chunk-RL2VWNJG.js +8 -0
  32. package/chunk-RZUDMW24.js +1 -0
  33. package/chunk-S2HNK7TK.js +1 -0
  34. package/chunk-S2YPSAFD.js +1 -0
  35. package/chunk-TGC6GY4N.js +1 -0
  36. package/chunk-UJYJZACJ.js +1 -0
  37. package/chunk-VAAR5GXC.js +1 -0
  38. package/chunk-VF6HT3GY.js +1 -0
  39. package/chunk-XBN37RRV.js +1 -0
  40. package/chunk-YC5LTBFM.js +1 -0
  41. package/index.html +15 -0
  42. package/main-TLCKWB2W.js +1 -0
  43. package/package.json +3 -40
  44. package/sparkle-ui-assets/fonts/InterTight-VariableFont_wght.ttf +0 -0
  45. package/sparkle-ui-assets/fonts/InterTight-VariableFont_wght.woff2 +0 -0
  46. package/styles-6W7ZCKNM.css +1 -0
  47. package/.dockerignore +0 -13
  48. package/.github/workflows/npm-publish.yml +0 -59
  49. package/.prettierignore +0 -13
  50. package/.prettierrc +0 -12
  51. package/.vscode/extensions.json +0 -4
  52. package/.vscode/launch.json +0 -13
  53. package/.vscode/settings.json +0 -8
  54. package/.vscode/tasks.json +0 -24
  55. package/Dockerfile +0 -38
  56. package/LICENSE +0 -21
  57. package/README.md +0 -27
  58. package/angular.json +0 -98
  59. package/bun.lockb +0 -0
  60. package/openapi-ts.config.ts +0 -13
  61. package/scripts/generate-package-json.ts +0 -15
  62. package/src/app/about/about.component.html +0 -15
  63. package/src/app/about/about.component.scss +0 -19
  64. package/src/app/about/about.component.ts +0 -13
  65. package/src/app/about/changelog/changelog.component.html +0 -3
  66. package/src/app/about/changelog/changelog.component.scss +0 -7
  67. package/src/app/about/changelog/changelog.component.ts +0 -18
  68. package/src/app/about/general/general.component.html +0 -9
  69. package/src/app/about/general/general.component.scss +0 -23
  70. package/src/app/about/general/general.component.ts +0 -30
  71. package/src/app/about/libraries/libraries.component.html +0 -3
  72. package/src/app/about/libraries/libraries.component.scss +0 -7
  73. package/src/app/about/libraries/libraries.component.ts +0 -18
  74. package/src/app/about/logs/logs.component.html +0 -5
  75. package/src/app/about/logs/logs.component.scss +0 -7
  76. package/src/app/about/logs/logs.component.ts +0 -18
  77. package/src/app/about/system-info/system-info.component.html +0 -3
  78. package/src/app/about/system-info/system-info.component.scss +0 -7
  79. package/src/app/about/system-info/system-info.component.ts +0 -17
  80. package/src/app/add-backup/add-backup.component.html +0 -29
  81. package/src/app/add-backup/add-backup.component.scss +0 -38
  82. package/src/app/add-backup/add-backup.component.ts +0 -14
  83. package/src/app/app.component.ts +0 -35
  84. package/src/app/app.config.ts +0 -23
  85. package/src/app/app.routes.ts +0 -121
  86. package/src/app/core/components/file-tree/file-tree.component.html +0 -87
  87. package/src/app/core/components/file-tree/file-tree.component.scss +0 -80
  88. package/src/app/core/components/file-tree/file-tree.component.ts +0 -485
  89. package/src/app/core/components/logo/logo.component.html +0 -22
  90. package/src/app/core/components/logo/logo.component.scss +0 -12
  91. package/src/app/core/components/logo/logo.component.ts +0 -11
  92. package/src/app/core/components/status-bar/status-bar.component.html +0 -38
  93. package/src/app/core/components/status-bar/status-bar.component.scss +0 -27
  94. package/src/app/core/components/status-bar/status-bar.component.ts +0 -33
  95. package/src/app/core/components/status-bar/status-bar.state.ts +0 -182
  96. package/src/app/core/components/toggle-card/toggle-card.component.html +0 -11
  97. package/src/app/core/components/toggle-card/toggle-card.component.scss +0 -49
  98. package/src/app/core/components/toggle-card/toggle-card.component.ts +0 -28
  99. package/src/app/core/interceptors/http.interceptor.ts +0 -69
  100. package/src/app/core/interceptors/websocket.interceptor.ts +0 -66
  101. package/src/app/core/openapi/core/ApiError.ts +0 -21
  102. package/src/app/core/openapi/core/ApiRequestOptions.ts +0 -21
  103. package/src/app/core/openapi/core/ApiResult.ts +0 -7
  104. package/src/app/core/openapi/core/OpenAPI.ts +0 -55
  105. package/src/app/core/openapi/core/request.ts +0 -337
  106. package/src/app/core/openapi/index.ts +0 -6
  107. package/src/app/core/openapi/schemas.gen.ts +0 -3611
  108. package/src/app/core/openapi/services.gen.ts +0 -1460
  109. package/src/app/core/openapi/types.gen.ts +0 -1510
  110. package/src/app/core/pipes/duration.pipe.ts +0 -27
  111. package/src/app/core/pipes/relative-time.pipe.ts +0 -14
  112. package/src/app/core/providers/dayjs.ts +0 -16
  113. package/src/app/core/services/dict.en.ts +0 -0
  114. package/src/app/core/services/dictionary.service.ts +0 -12
  115. package/src/app/core/services/localstorage.token.ts +0 -82
  116. package/src/app/core/services/password-generator.service.ts +0 -39
  117. package/src/app/core/services/relay-websocket.service.ts +0 -268
  118. package/src/app/core/services/timespan-literals.service.ts +0 -25
  119. package/src/app/core/states/app-auth.state.ts +0 -87
  120. package/src/app/core/states/backups.state.ts +0 -71
  121. package/src/app/core/states/relayconfig.state.ts +0 -23
  122. package/src/app/core/states/sysinfo.state.ts +0 -31
  123. package/src/app/core/states/tasks.state.ts +0 -34
  124. package/src/app/core/types/subscribed.ts +0 -7
  125. package/src/app/core/validators/custom.validators.ts +0 -120
  126. package/src/app/create-edit-backup/create-edit-backup.component.html +0 -28
  127. package/src/app/create-edit-backup/create-edit-backup.component.scss +0 -15
  128. package/src/app/create-edit-backup/create-edit-backup.component.ts +0 -33
  129. package/src/app/create-edit-backup/create-edit-backup.state.ts +0 -582
  130. package/src/app/create-edit-backup/destination/destination.component.html +0 -367
  131. package/src/app/create-edit-backup/destination/destination.component.scss +0 -85
  132. package/src/app/create-edit-backup/destination/destination.component.ts +0 -249
  133. package/src/app/create-edit-backup/destination/destination.config.ts +0 -103
  134. package/src/app/create-edit-backup/destination/destination.mapper.ts +0 -177
  135. package/src/app/create-edit-backup/general/general.component.html +0 -108
  136. package/src/app/create-edit-backup/general/general.component.scss +0 -34
  137. package/src/app/create-edit-backup/general/general.component.ts +0 -136
  138. package/src/app/create-edit-backup/options/options.component.html +0 -95
  139. package/src/app/create-edit-backup/options/options.component.scss +0 -42
  140. package/src/app/create-edit-backup/options/options.component.ts +0 -113
  141. package/src/app/create-edit-backup/schedule/schedule.component.html +0 -102
  142. package/src/app/create-edit-backup/schedule/schedule.component.scss +0 -38
  143. package/src/app/create-edit-backup/schedule/schedule.component.ts +0 -137
  144. package/src/app/create-edit-backup/source-data/source-data.component.html +0 -93
  145. package/src/app/create-edit-backup/source-data/source-data.component.scss +0 -41
  146. package/src/app/create-edit-backup/source-data/source-data.component.ts +0 -114
  147. package/src/app/home/backup.const.ts +0 -167
  148. package/src/app/home/home.component.html +0 -98
  149. package/src/app/home/home.component.scss +0 -88
  150. package/src/app/home/home.component.ts +0 -54
  151. package/src/app/layout/layout.component.html +0 -64
  152. package/src/app/layout/layout.component.scss +0 -24
  153. package/src/app/layout/layout.component.ts +0 -51
  154. package/src/app/layout/layout.state.ts +0 -70
  155. package/src/app/login/login.component.html +0 -12
  156. package/src/app/login/login.component.scss +0 -25
  157. package/src/app/login/login.component.ts +0 -34
  158. package/src/app/logout/logout.component.html +0 -1
  159. package/src/app/logout/logout.component.scss +0 -3
  160. package/src/app/logout/logout.component.ts +0 -18
  161. package/src/app/restore/restore.component.html +0 -56
  162. package/src/app/restore/restore.component.scss +0 -37
  163. package/src/app/restore/restore.component.ts +0 -55
  164. package/src/app/restore-flow/options/options.component.html +0 -57
  165. package/src/app/restore-flow/options/options.component.scss +0 -16
  166. package/src/app/restore-flow/options/options.component.ts +0 -58
  167. package/src/app/restore-flow/restore-flow.component.html +0 -14
  168. package/src/app/restore-flow/restore-flow.component.scss +0 -15
  169. package/src/app/restore-flow/restore-flow.component.ts +0 -23
  170. package/src/app/restore-flow/restore-flow.state.ts +0 -120
  171. package/src/app/restore-flow/select-files/select-files.component.html +0 -42
  172. package/src/app/restore-flow/select-files/select-files.component.scss +0 -16
  173. package/src/app/restore-flow/select-files/select-files.component.ts +0 -129
  174. package/src/app/settings/settings.component.html +0 -256
  175. package/src/app/settings/settings.component.scss +0 -106
  176. package/src/app/settings/settings.component.ts +0 -475
  177. package/src/environments/environment-token.ts +0 -4
  178. package/src/environments/environment.prod.ts +0 -6
  179. package/src/environments/environment.ts +0 -6
  180. package/src/index.html +0 -15
  181. package/src/main.ts +0 -6
  182. package/src/proxy.conf.mjs +0 -6
  183. package/src/styles.scss +0 -41
  184. package/tsconfig.app.json +0 -15
  185. package/tsconfig.json +0 -30
  186. /package/{src/assets → assets}/duplicati-logo.png +0 -0
  187. /package/{src/assets → assets}/duplicati-logo.svg +0 -0
  188. /package/{src/assets → assets}/favicon.ico +0 -0
  189. /package/{src/assets → assets}/images/backup.png +0 -0
  190. /package/{src/assets → assets}/images/dark-theme.png +0 -0
  191. /package/{src/assets → assets}/images/folder.png +0 -0
  192. /package/{src/assets → assets}/images/light-theme.png +0 -0
  193. /package/{src/assets → assets}/images/new-backup.png +0 -0
  194. /package/{src/assets → assets}/images/restore.png +0 -0
  195. /package/{src/assets → assets}/images/system-theme.png +0 -0
  196. /package/{src/assets → assets}/spk.css +0 -0
  197. /package/{src/assets → assets}/spk.woff2 +0 -0
@@ -1,27 +0,0 @@
1
- import { inject, Pipe, type PipeTransform } from '@angular/core';
2
- import { DAYJS } from '../providers/dayjs';
3
-
4
- @Pipe({
5
- name: 'durationFormat',
6
- standalone: true,
7
- })
8
- export class DurationFormatPipe implements PipeTransform {
9
- #dayjs = inject(DAYJS);
10
-
11
- transform(value: string | undefined | null, ...args: unknown[]): unknown {
12
- if (!value) return '';
13
-
14
- const _value = value.split('.')[0];
15
- const hours = parseInt(_value.split(':')[0]) ?? 0;
16
- const minutes = parseInt(_value.split(':')[1]) ?? 0;
17
- const seconds = parseInt(_value.split(':')[2]) ?? 0;
18
-
19
- return this.#dayjs
20
- .duration({
21
- hours,
22
- minutes,
23
- seconds,
24
- })
25
- .humanize();
26
- }
27
- }
@@ -1,14 +0,0 @@
1
- import { inject, Pipe, type PipeTransform } from '@angular/core';
2
- import { DAYJS } from '../providers/dayjs';
3
-
4
- @Pipe({
5
- name: 'appRelativeTime',
6
- standalone: true,
7
- })
8
- export class RelativeTimePipe implements PipeTransform {
9
- #dayjs = inject(DAYJS);
10
-
11
- transform(value: string, ...args: unknown[]): unknown {
12
- return this.#dayjs(value).fromNow();
13
- }
14
- }
@@ -1,16 +0,0 @@
1
- import { InjectionToken, Provider } from '@angular/core';
2
- import dayjs from 'dayjs';
3
- import duration from 'dayjs/plugin/duration';
4
- import relativeTime from 'dayjs/plugin/relativeTime';
5
-
6
- export const DAYJS = new InjectionToken<typeof dayjs>('Dayjs');
7
-
8
- export const DayJsProvider: Provider = {
9
- provide: DAYJS,
10
- useFactory: () => {
11
- dayjs.extend(relativeTime);
12
- dayjs.extend(duration);
13
-
14
- return dayjs;
15
- },
16
- };
File without changes
@@ -1,12 +0,0 @@
1
- import { Injectable, signal } from '@angular/core';
2
-
3
- type Dictionary = {};
4
-
5
- @Injectable({
6
- providedIn: 'root',
7
- })
8
- export class DictionaryService {
9
- #activeDictionary = signal(null);
10
-
11
- activeDictionary = this.#activeDictionary.asReadonly();
12
- }
@@ -1,82 +0,0 @@
1
- import { isPlatformBrowser, isPlatformServer } from '@angular/common';
2
- import { Injectable, PLATFORM_ID, inject } from '@angular/core';
3
-
4
- @Injectable()
5
- export class LOCALSTORAGE implements Storage {
6
- private platformId = inject(PLATFORM_ID);
7
- private STORAGE_VERSION = 'v1';
8
- private APP_NAME = 'duplicati';
9
-
10
- getItemParsed<T>(key: string, presistedThroughClear = false): T | null {
11
- if (isPlatformServer(this.platformId)) return null;
12
-
13
- return JSON.parse(
14
- localStorage.getItem(
15
- `${this.STORAGE_VERSION}:${presistedThroughClear ? 'presist-duplicati' : this.APP_NAME}:${key}`
16
- ) ?? 'null'
17
- );
18
- }
19
-
20
- setItemParsed<T>(key: string, value: T, presistThroughClear = false): void {
21
- if (isPlatformServer(this.platformId)) return;
22
-
23
- return localStorage.setItem(
24
- `${this.STORAGE_VERSION}:${presistThroughClear ? 'presist-duplicati' : this.APP_NAME}:${key}`,
25
- JSON.stringify(value)
26
- );
27
- }
28
-
29
- get length(): number {
30
- return isPlatformBrowser(this.platformId) ? localStorage.length : 0;
31
- }
32
-
33
- getItem(key: string): string | null {
34
- if (isPlatformServer(this.platformId)) return null;
35
-
36
- return localStorage.getItem(`${this.STORAGE_VERSION}:${this.APP_NAME}:${key}`) ?? null;
37
- }
38
-
39
- setItem(key: string, value: string): void {
40
- if (isPlatformServer(this.platformId)) return;
41
-
42
- return localStorage.setItem(`${this.STORAGE_VERSION}:${this.APP_NAME}:${key}`, value);
43
- }
44
-
45
- removeItem(key: string): void {
46
- if (isPlatformServer(this.platformId)) return;
47
-
48
- localStorage.removeItem(`${this.STORAGE_VERSION}:${this.APP_NAME}:${key}`);
49
- }
50
-
51
- clearAllNotCurrentVersion(): void {
52
- if (isPlatformServer(this.platformId)) return;
53
-
54
- for (var key in localStorage) {
55
- if (!key.startsWith(this.STORAGE_VERSION)) {
56
- localStorage.removeItem(key);
57
- }
58
- }
59
- }
60
-
61
- clearAll(): void {
62
- if (isPlatformServer(this.platformId)) return;
63
-
64
- localStorage.clear();
65
- }
66
-
67
- clear(): void {
68
- if (isPlatformServer(this.platformId)) return;
69
-
70
- for (var key in localStorage) {
71
- if (key.includes(this.APP_NAME) && !key.includes(`presist-${this.APP_NAME}`)) {
72
- localStorage.removeItem(key);
73
- }
74
- }
75
- }
76
-
77
- key(index: number): string | null {
78
- if (isPlatformServer(this.platformId)) return null;
79
-
80
- return localStorage.key(index);
81
- }
82
- }
@@ -1,39 +0,0 @@
1
- import { Injectable } from '@angular/core';
2
-
3
- const DEFAULT_PASSWORD_PATTERN = /[a-zA-Z0-9_\-\+\.]/;
4
-
5
- @Injectable({
6
- providedIn: 'root',
7
- })
8
- export class PasswordGeneratorService {
9
- generate(length: number, pattern = DEFAULT_PASSWORD_PATTERN) {
10
- const _self = this;
11
-
12
- return Array.apply(null, { length: length } as any)
13
- .map(() => {
14
- let result;
15
- while (true) {
16
- result = String.fromCharCode(_self.#getRandomByte());
17
-
18
- if (pattern.test(result)) {
19
- return result;
20
- }
21
- }
22
- }, this)
23
- .join('');
24
- }
25
-
26
- #getRandomByte() {
27
- if (window.crypto && window.crypto.getRandomValues) {
28
- const result = new Uint8Array(1);
29
- window.crypto.getRandomValues(result);
30
- return result[0];
31
- } else if ((window as any).msCrypto && (window as any).msCrypto.getRandomValues) {
32
- const result = new Uint8Array(1);
33
- (window as any).msCrypto.getRandomValues(result);
34
- return result[0];
35
- } else {
36
- return Math.floor(Math.random() * 256);
37
- }
38
- }
39
- }
@@ -1,268 +0,0 @@
1
- import { effect, inject, Injectable, signal } from '@angular/core';
2
- import { ENVIRONMENT_TOKEN } from '../../../environments/environment-token';
3
-
4
- type SocketProtocolState = 'disconnected' | 'connect' | 'welcome' | 'authenticated' | 'error';
5
-
6
- type MessageType = 'authportal' | 'list' | 'welcome' | 'command' | 'warning' | 'auth';
7
-
8
- type MessageEnvelope = {
9
- from: string;
10
- to: string;
11
- type: MessageType;
12
- messageId: string;
13
- payload: string | null;
14
- };
15
-
16
- type AuthRequestMessage = {
17
- token: string;
18
- publicKey: string;
19
- clientVersion: string;
20
- protocolVersion: number;
21
- };
22
-
23
- type AuthResponseMessage = {
24
- accepted: boolean | null;
25
- willReplaceToken: boolean | null;
26
- newToken: string | null;
27
- };
28
-
29
- export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
30
-
31
- type CommandRequest = {
32
- method: RequestMethod;
33
- path: string;
34
- body: string | null;
35
- headers: { [key: string]: string } | null;
36
- };
37
-
38
- type PromiseResolver = {
39
- resolve: (value: CommandResponse | PromiseLike<CommandResponse>) => void;
40
- reject: (reason?: any) => void;
41
- timer: number;
42
- };
43
-
44
- export type CommandResponse = {
45
- code: number;
46
- body: string | null;
47
- headers: { [key: string]: string } | null;
48
- };
49
-
50
- const ClientVersion = '1.0.0';
51
- const ClientId = 'portal-proxy-client';
52
- const ProtocolVersion = 1;
53
-
54
- @Injectable({
55
- providedIn: 'root',
56
- })
57
- export class RelayWebsocketService {
58
- #MIN_POLL_INTERVAL = 1000;
59
- #env = inject(ENVIRONMENT_TOKEN);
60
- #wsState = signal<SocketProtocolState>('disconnected');
61
- #reconnectInterval = signal<number | null>(this.#MIN_POLL_INTERVAL);
62
- #isReconnecting = signal(false);
63
- #isConnectedToMachineServer = signal(false);
64
- #reconnectToken = signal<string | null>(null);
65
-
66
- isConnectedToMachineServer = this.#isConnectedToMachineServer.asReadonly();
67
- wsState = this.#wsState.asReadonly();
68
-
69
- // TODO:
70
- // - Auto reconnect
71
-
72
- #queuedCommands: MessageEnvelope[] = [];
73
- #pendingCommands: { [key: string]: PromiseResolver } = {};
74
- #ws: WebSocket | null = null;
75
- #textDecoder = new TextDecoder('utf-8');
76
- #activeInterval: number | null = null;
77
-
78
- #e = effect(() => {
79
- const isReconnecting = this.#isReconnecting();
80
- const reconnectInterval = this.#reconnectInterval();
81
- const reconnectToken = this.#reconnectToken();
82
-
83
- if (!reconnectToken || !isReconnecting || !reconnectInterval || reconnectInterval < this.#MIN_POLL_INTERVAL) {
84
- this.#activeInterval && window.clearInterval(this.#activeInterval);
85
- return;
86
- }
87
-
88
- this.#activeInterval = window.setInterval(() => {
89
- this.connectToMachineServer(reconnectToken);
90
- }, reconnectInterval);
91
- });
92
-
93
- utf8Atob(str: string) {
94
- const decodedData = Uint8Array.from(atob(str), (c) => c.charCodeAt(0));
95
-
96
- const stringifiedJson = this.#textDecoder.decode(decodedData);
97
- if (stringifiedJson)
98
- return JSON.parse(stringifiedJson);
99
- else
100
- return null;
101
- }
102
-
103
- connectToMachineServer(token: string, options?: { reconnect?: boolean }) {
104
- const reconnect = options?.reconnect ?? false;
105
-
106
- if (this.#ws && this.wsState() === 'authenticated' && !reconnect) {
107
- return;
108
- }
109
-
110
- if (this.#ws && this.wsState() !== 'authenticated') {
111
- this.#ws.close();
112
- }
113
-
114
- this.#ws = new WebSocket(this.#env.machineServerUrl);
115
-
116
- this.#ws.onopen = () => {
117
- this.#wsState.set('connect');
118
- this.#isConnectedToMachineServer.set(true);
119
- this.#isReconnecting.set(false);
120
- this.#reconnectToken.set(null);
121
- };
122
-
123
- this.#ws.onclose = () => {
124
- // Should we reconnect automatically?
125
- this.#wsState.set('disconnected');
126
- this.#isConnectedToMachineServer.set(false);
127
- this.#reconnectToken.set(token);
128
- this.#isReconnecting.set(true);
129
- };
130
-
131
- this.#ws.onerror = () => {
132
- this.#wsState.set('error');
133
- };
134
-
135
- this.#ws.onmessage = (event) => {
136
- const data = JSON.parse(event.data) as MessageEnvelope;
137
-
138
- if (this.wsState() === 'connect') {
139
- if (data.type !== 'welcome') {
140
- this.disconnectFromMachineServer(`Unexpected message type, expected 'welcome' but got '${data.type}'`);
141
- return;
142
- }
143
-
144
- const authContent: AuthRequestMessage = {
145
- token: token,
146
- publicKey: '',
147
- clientVersion: ClientVersion,
148
- protocolVersion: ProtocolVersion,
149
- };
150
- const authRequest = this.createResponse(data, 'authportal', authContent);
151
- this.#wsState.set('welcome');
152
- this.#ws?.send(authRequest);
153
- } else if (this.wsState() === 'welcome') {
154
- if (data.type !== 'authportal') {
155
- this.disconnectFromMachineServer(`Unexpected message type, expected 'authportal' but got '${data.type}'`);
156
- return;
157
- }
158
-
159
- var payload = JSON.parse(data.payload ?? '') as AuthResponseMessage;
160
- if (payload.accepted === false) {
161
- this.disconnectFromMachineServer('Authentication failed');
162
- return;
163
- }
164
-
165
- this.#wsState.set('authenticated');
166
- this.#isConnectedToMachineServer.set(true);
167
-
168
- // Send queued commands
169
- while (this.#queuedCommands.length > 0) {
170
- const message = this.#queuedCommands.pop();
171
- // Only send if this request has not timed out yet
172
- if (message && this.#pendingCommands[message.messageId]) this.activateCommand(message);
173
- }
174
- } else if (this.wsState() === 'authenticated') {
175
- if (data.type !== 'command') {
176
- console.warn(`Unexpected message type, expected 'command' but got '${data.type}'`);
177
- return;
178
- }
179
-
180
- const payload = JSON.parse(data.payload ?? '') as CommandResponse;
181
- payload.body = payload?.body == null ? null : this.utf8Atob(payload.body);
182
- const f = this.#pendingCommands[data.messageId];
183
- if (f) {
184
- delete this.#pendingCommands[data.messageId];
185
- window.clearTimeout(f.timer);
186
- f.resolve(payload);
187
- }
188
- }
189
- };
190
- }
191
-
192
- private disconnectFromMachineServer(errorMessage: string | null = null) {
193
- if (errorMessage) console.error(errorMessage);
194
-
195
- this.#ws?.close();
196
- this.#ws = null;
197
- this.#wsState.set('disconnected');
198
- this.#isConnectedToMachineServer.set(false);
199
- }
200
-
201
- private createResponse(request: MessageEnvelope, type: MessageType, payload: any) {
202
- const response: MessageEnvelope = {
203
- from: ClientId,
204
- to: request.from,
205
- type: type,
206
- messageId: request.messageId,
207
- payload: JSON.stringify(payload),
208
- };
209
-
210
- return JSON.stringify(response);
211
- }
212
-
213
- sendCommand(
214
- token: string,
215
- clientId: string,
216
- method: RequestMethod,
217
- path: string,
218
- body: string | null,
219
- headers: { [key: string]: string } | null,
220
- timeout: number = 5000
221
- ) {
222
- if (this.wsState() === 'disconnected' || this.wsState() === 'error') {
223
- // Connect to the machine server if we are not connected
224
- this.connectToMachineServer(token);
225
- }
226
-
227
- return new Promise<CommandResponse>((resolve, reject) => {
228
- const messageId = crypto.randomUUID();
229
-
230
- const request: CommandRequest = {
231
- method,
232
- path,
233
- body,
234
- headers,
235
- };
236
-
237
- const message: MessageEnvelope = {
238
- from: ClientId,
239
- to: clientId,
240
- type: 'command',
241
- messageId: messageId,
242
- payload: JSON.stringify(request),
243
- };
244
-
245
- const f: PromiseResolver = {
246
- resolve,
247
- reject,
248
- timer: window.setTimeout(() => {
249
- const f = this.#pendingCommands[messageId];
250
- if (f) {
251
- delete this.#pendingCommands[messageId];
252
- f.reject('Timeout');
253
- }
254
- }, timeout),
255
- };
256
-
257
- this.#pendingCommands[messageId] = f;
258
-
259
- // If we are not yet connected to the machine server, queue the command
260
- if (this.#isConnectedToMachineServer()) this.activateCommand(message);
261
- else this.#queuedCommands.push(message);
262
- });
263
- }
264
-
265
- private activateCommand(message: MessageEnvelope) {
266
- this.#ws?.send(JSON.stringify(message));
267
- }
268
- }
@@ -1,25 +0,0 @@
1
- import { Injectable } from '@angular/core';
2
-
3
- type Unit = 's' | 'm' | 'h' | 'D' | 'W' | 'M' | 'Y';
4
-
5
- @Injectable({
6
- providedIn: 'root',
7
- })
8
- export class TimespanLiteralsService {
9
- fromString(str: string | null | undefined) {
10
- if (!str) return null;
11
-
12
- const res = str.match(/(\d+)([smhDWMY])/);
13
-
14
- return (
15
- res && {
16
- value: parseInt(res[1]),
17
- unit: res[2] as Unit,
18
- }
19
- );
20
- }
21
-
22
- toString(value: number, unit: Unit) {
23
- return `${value}${unit}`;
24
- }
25
- }
@@ -1,87 +0,0 @@
1
- import { effect, inject, Injectable, signal } from '@angular/core';
2
- import { Router } from '@angular/router';
3
- import { take, tap } from 'rxjs';
4
- import { DuplicatiServerService } from '../openapi';
5
- import { LOCALSTORAGE } from '../services/localstorage.token';
6
-
7
- @Injectable({
8
- providedIn: 'root',
9
- })
10
- export class AppAuthState {
11
- #ls = inject(LOCALSTORAGE);
12
- #router = inject(Router);
13
- #dupServer = inject(DuplicatiServerService);
14
- #token = signal<string | null>(this.#ls.getItemParsed<string>('token') ?? null);
15
- #tokenSetTime = signal<number | null>(this.#ls.getItemParsed<number>('tokenSetTime') ?? null);
16
-
17
- tokenSetTime = this.#tokenSetTime.asReadonly();
18
- token = this.#token.asReadonly();
19
-
20
- #tokenEffect = effect(
21
- () => {
22
- if (!this.#token()) {
23
- this.#ls.clearAll();
24
- this.#router.navigate(['/login']);
25
- return;
26
- }
27
- const now = Date.now();
28
- this.#ls.setItemParsed('tokenSetTime', now);
29
- this.#ls.setItemParsed('token', this.#token());
30
- this.#tokenSetTime.set(this.#token() ? now : null);
31
- },
32
- { allowSignalWrites: true }
33
- );
34
-
35
- login(pass = 'helloworld') {
36
- return this.#dupServer
37
- .postApiV1AuthLogin({
38
- requestBody: {
39
- Password: pass,
40
- RememberMe: true,
41
- },
42
- })
43
- .pipe(
44
- take(1),
45
- tap((res) => this.#token.set(res.AccessToken ?? null))
46
- );
47
- }
48
-
49
- refreshToken() {
50
- return this.#dupServer.postApiV1AuthRefresh().pipe(
51
- take(1),
52
- tap((res) => this.#token.set(res.AccessToken ?? null))
53
- );
54
- }
55
-
56
- logout() {
57
- this.#dupServer
58
- .postApiV1AuthRefreshLogout()
59
- .pipe(
60
- take(1),
61
- tap((_) => {
62
- this.#token.set(null);
63
- this.#ls.clearAll();
64
- this.#router.navigate(['/login']);
65
- })
66
- )
67
- .subscribe({
68
- error: (_) => {
69
- this.#token.set(null);
70
- this.#ls.clearAll();
71
- this.#router.navigate(['/login']);
72
- },
73
- });
74
- }
75
-
76
- // reLogin() {
77
-
78
- // return this.#dupServer.postApiV1AuthRefreshLogout().pipe(
79
- // take(1),
80
- // tap((_) => {
81
- // this.#token.set(null);
82
- // this.#ls.clearAll();
83
- // })
84
- // .
85
- // );
86
- // }
87
- }
@@ -1,71 +0,0 @@
1
- import { inject, Injectable, signal } from '@angular/core';
2
- import { finalize, take } from 'rxjs';
3
- import { DuplicatiServerService } from '../openapi';
4
- import { Subscribed } from '../types/subscribed';
5
-
6
- export type BackupRes = Subscribed<ReturnType<DuplicatiServerService['getApiV1Backups']>>;
7
- export type Backup = BackupRes[0];
8
-
9
- @Injectable({
10
- providedIn: 'root',
11
- })
12
- export class BackupsState {
13
- #dupServer = inject(DuplicatiServerService);
14
- #timestamp: number | null = null;
15
-
16
- backups = signal<BackupRes>([]);
17
- backupsLoading = signal(false);
18
-
19
- getBackupById(id: string) {
20
- return this.backups().find((x) => x.Backup?.ID === id) ?? null;
21
- }
22
-
23
- getBackups(refresh = false) {
24
- if (this.#timestamp && Date.now() - this.#timestamp < 100) {
25
- return;
26
- }
27
-
28
- this.#timestamp = Date.now();
29
-
30
- this.backupsLoading.set(true);
31
-
32
- this.#dupServer
33
- .getApiV1Backups()
34
- .pipe(
35
- take(1),
36
- finalize(() => this.backupsLoading.set(false))
37
- )
38
- .subscribe({
39
- next: (res) => {
40
- this.backups.set(res);
41
- },
42
- error: (err) => {
43
- console.error(err);
44
- },
45
- });
46
- }
47
-
48
- startBackup(id: string) {
49
- this.#dupServer
50
- .postApiV1BackupByIdStart({
51
- id,
52
- })
53
- .subscribe({
54
- next: () => {
55
- this.getBackups();
56
- },
57
- });
58
- }
59
-
60
- deleteBackup(id: string) {
61
- this.#dupServer
62
- .deleteApiV1BackupById({
63
- id,
64
- })
65
- .subscribe({
66
- next: () => {
67
- this.getBackups();
68
- },
69
- });
70
- }
71
- }
@@ -1,23 +0,0 @@
1
- import { Injectable, signal } from '@angular/core';
2
-
3
- export type Relayconfig = {
4
- accessToken: string;
5
- clientId: string;
6
- };
7
-
8
- @Injectable({
9
- providedIn: 'root',
10
- })
11
- export class RelayconfigState {
12
- #config = signal<Relayconfig | null>(null);
13
-
14
- config = this.#config.asReadonly();
15
-
16
- setConfig(config: Relayconfig) {
17
- this.#config.set(config);
18
- }
19
-
20
- clearConfig() {
21
- this.#config.set(null);
22
- }
23
- }