@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,167 @@
1
+ export type BackupTemp = {
2
+ id: string;
3
+ organizationId: string;
4
+ machineId: string;
5
+ machineName: string;
6
+ endTime: string;
7
+ duration: string | null;
8
+ source: number | null;
9
+ destination: number | null;
10
+ version: string | null;
11
+ machineTags: string[];
12
+ latestBackupReports: {
13
+ backupReportId: string;
14
+ endTime: string;
15
+ status: string | null;
16
+ }[];
17
+ };
18
+
19
+ export const BACKUPS: BackupTemp[] = [
20
+ {
21
+ id: '08eca0fb-8f4d-408d-a5db-7408d7ad5922-1234test',
22
+ organizationId: '08eca0fb-8f4d-408d-a5db-7408d7ad5922',
23
+ machineId: '1234test',
24
+ machineName: '1234test2',
25
+ endTime: '2024-07-08T05:00:02.176194+00:00',
26
+ duration: '00:00:01.8431440',
27
+ source: 73193356,
28
+ destination: 69703215,
29
+ version: '1.0.0.0 (Current)',
30
+ machineTags: [],
31
+ latestBackupReports: [
32
+ {
33
+ backupReportId: '76199ce1-b9e8-47d2-9a66-606ff9983b35',
34
+ endTime: '2024-07-04T20:26:19.361749+00:00',
35
+ status: 'Success',
36
+ },
37
+ {
38
+ backupReportId: '954cefb9-aaeb-41ed-92a0-0759d533e1ae',
39
+ endTime: '2024-07-04T20:28:30.435824+00:00',
40
+ status: 'Success',
41
+ },
42
+ {
43
+ backupReportId: '035ac9ea-44a1-4747-bf7d-d23daf556a50',
44
+ endTime: '2024-07-04T20:35:53.290829+00:00',
45
+ status: 'Success',
46
+ },
47
+ {
48
+ backupReportId: 'e9597e50-882b-40ff-8f0d-d52639028665',
49
+ endTime: '2024-07-04T20:37:13.605541+00:00',
50
+ status: 'Success',
51
+ },
52
+ {
53
+ backupReportId: '22f18ac8-9ee4-4454-876f-e2b43a9cd6f4',
54
+ endTime: '2024-07-06T09:06:11.920107+00:00',
55
+ status: 'Success',
56
+ },
57
+ {
58
+ backupReportId: '560d42e3-fa66-4fbc-9fbb-5941ec26bdf2',
59
+ endTime: '2024-07-06T10:10:33.107455+00:00',
60
+ status: 'Success',
61
+ },
62
+ {
63
+ backupReportId: '9bf75628-1d72-4dfd-a777-6a4ef4fad4a4',
64
+ endTime: '2024-07-06T10:19:33.706844+00:00',
65
+ status: 'Success',
66
+ },
67
+ {
68
+ backupReportId: 'f35647c7-8bf9-4b45-b103-a285f247ac8a',
69
+ endTime: '2024-07-06T19:58:50.284511+00:00',
70
+ status: 'Error',
71
+ },
72
+ {
73
+ backupReportId: '3bb116c5-7046-4820-adaf-5f8ef461643f',
74
+ endTime: '2024-07-07T08:32:55.142756+00:00',
75
+ status: 'Error',
76
+ },
77
+ {
78
+ backupReportId: '4da96a66-cf6e-461f-abd1-efa300b3e071',
79
+ endTime: '2024-07-08T05:00:03.069452+00:00',
80
+ status: 'Success',
81
+ },
82
+ ],
83
+ },
84
+ {
85
+ id: '08eca0fb-8f4d-408d-a5db-7408d7ad5922-UNKNOWN',
86
+ organizationId: '08eca0fb-8f4d-408d-a5db-7408d7ad5922',
87
+ machineId: 'UNKNOWN',
88
+ machineName: 'UNKNOWN',
89
+ endTime: '2024-07-04T13:53:55.989892+00:00',
90
+ duration: null,
91
+ source: null,
92
+ destination: null,
93
+ version: null,
94
+ machineTags: [],
95
+ latestBackupReports: [
96
+ {
97
+ backupReportId: 'db8870da-a979-4eb5-b880-0213a6c0c6dd',
98
+ endTime: '2024-06-19T13:29:46.249283+00:00',
99
+ status: null,
100
+ },
101
+ {
102
+ backupReportId: '89e85445-860b-4f9a-b2bd-756682955993',
103
+ endTime: '2024-06-19T13:29:46.42688+00:00',
104
+ status: 'Success',
105
+ },
106
+ {
107
+ backupReportId: '578c6b63-2e32-4116-a639-f5b9a7cb0c5b',
108
+ endTime: '2024-06-19T13:29:46.580843+00:00',
109
+ status: null,
110
+ },
111
+ {
112
+ backupReportId: '4c3d7796-a08e-4916-969a-c7b0721749b3',
113
+ endTime: '2024-06-19T13:29:46.750381+00:00',
114
+ status: null,
115
+ },
116
+ {
117
+ backupReportId: '50d8a246-36eb-46f2-bcf6-602ace2421ee',
118
+ endTime: '2024-06-19T13:29:47.015853+00:00',
119
+ status: 'Success',
120
+ },
121
+ {
122
+ backupReportId: '247ba9f4-4a03-4b6e-9dcd-8b76182c326b',
123
+ endTime: '2024-07-04T13:53:53.921397+00:00',
124
+ status: null,
125
+ },
126
+ {
127
+ backupReportId: '94ba04e5-dce3-48ee-b41e-4ce1a0222df1',
128
+ endTime: '2024-07-04T13:53:55.538234+00:00',
129
+ status: 'Success',
130
+ },
131
+ {
132
+ backupReportId: '3115d9e3-952c-4472-be45-6b246ce8d5a6',
133
+ endTime: '2024-07-04T13:53:55.729932+00:00',
134
+ status: null,
135
+ },
136
+ {
137
+ backupReportId: 'de62f4a0-091e-416c-88b3-37f4c481efbd',
138
+ endTime: '2024-07-04T13:53:55.989892+00:00',
139
+ status: null,
140
+ },
141
+ {
142
+ backupReportId: '0172ccab-e2c5-43d5-acd2-67266b9ce9e6',
143
+ endTime: '2024-07-04T13:53:56.279706+00:00',
144
+ status: 'Success',
145
+ },
146
+ ],
147
+ },
148
+ {
149
+ id: '08eca0fb-8f4d-408d-a5db-7408d7ad5922-b3c774b874914bc98bc6964727e7e7c4',
150
+ organizationId: '08eca0fb-8f4d-408d-a5db-7408d7ad5922',
151
+ machineId: 'b3c774b874914bc98bc6964727e7e7c4',
152
+ machineName: 'hello world',
153
+ endTime: '2024-05-29T13:37:44.775107+00:00',
154
+ duration: '00:00:01.0571230',
155
+ source: 70109345,
156
+ destination: 67349385,
157
+ version: '1.0.0.0 (Current)',
158
+ machineTags: [],
159
+ latestBackupReports: [
160
+ {
161
+ backupReportId: '0d02b3e4-384e-4aec-9d16-a4bac601e05e',
162
+ endTime: '2024-05-29T13:37:45.62437+00:00',
163
+ status: 'Success',
164
+ },
165
+ ],
166
+ },
167
+ ];
@@ -0,0 +1,98 @@
1
+ <app-status-bar />
2
+
3
+ <section>
4
+ <header>
5
+ <spk-card class="type-a" [routerLink]="['/add-backup']">
6
+ <img src="/assets/images/backup.png" alt="" width="48px" />
7
+ <div class="text">
8
+ <h3>Backups</h3>
9
+ <p>Backups are stored in the cloud and can be restored at any time.</p>
10
+ </div>
11
+ <button spk-button>
12
+ Add
13
+ <spk-icon>plus</spk-icon>
14
+ </button>
15
+ </spk-card>
16
+
17
+ <spk-card class="type-a" [routerLink]="['/restore']">
18
+ <img src="/assets/images/restore.png" alt="" width="48px" />
19
+ <div class="text">
20
+ <h3>Restores</h3>
21
+ <p>Restores are stored in the cloud and can be restored at any time.</p>
22
+ </div>
23
+ <button spk-button>
24
+ Start
25
+ <spk-icon>arrow-right</spk-icon>
26
+ </button>
27
+ </spk-card>
28
+ </header>
29
+
30
+ <div class="content">
31
+ <h2>My backups</h2>
32
+ <div class="backups">
33
+ @if (backups().length) {
34
+ @for (backup of backups(); track $index) {
35
+ <div class="backup">
36
+ <div class="text">
37
+ <h3>
38
+ {{ backup.Backup?.Name ?? 'Backup name missing' }}
39
+ <spk-icon>database</spk-icon>
40
+ </h3>
41
+
42
+ <p>
43
+ <span>Last successful backup:</span>
44
+ {{ backup.Backup?.Metadata?.['LastBackupDate'] | date: 'short' }}
45
+ @if (backup.Backup?.Metadata?.['LastBackupDuration']) {
46
+ - (took {{ backup.Backup?.Metadata?.['LastBackupDuration'] | durationFormat }})
47
+ } @else {
48
+ N/A
49
+ }
50
+ </p>
51
+
52
+ <p>
53
+ <span>Source:</span>
54
+ @if (backup.Backup?.Metadata?.['SourceSizeString']) {
55
+ {{ backup.Backup?.Metadata?.['SourceSizeString'] }}
56
+ } @else {
57
+ N/A
58
+ }
59
+ </p>
60
+
61
+ <p>
62
+ <span>Destination:</span>
63
+ @if (backup.Backup?.Metadata?.['TargetSizeString']) {
64
+ {{ backup.Backup?.Metadata?.['TargetSizeString'] }}
65
+ } @else {
66
+ N/A
67
+ }
68
+ </p>
69
+ </div>
70
+
71
+ @if (backup.Backup?.Metadata?.['BackupListCount']) {
72
+ <spk-chip class="simple success">{{ backup.Backup?.Metadata?.['BackupListCount'] }} Version</spk-chip>
73
+ } @else {
74
+ <spk-chip class="simple">No version yet</spk-chip>
75
+ }
76
+
77
+ <div class="actions">
78
+ <button spk-button (click)="startBackup($any(backup.Backup?.ID))">
79
+ <spk-icon>play-circle</spk-icon>
80
+ Start
81
+ </button>
82
+ <button spk-button [routerLink]="['/backup', backup.Backup?.ID]">
83
+ <spk-icon>pencil-circle</spk-icon>
84
+ Edit
85
+ </button>
86
+ <button spk-button (click)="deleteBackup(backup.Backup?.ID)">
87
+ <spk-icon>x-circle</spk-icon>
88
+ Delete
89
+ </button>
90
+ </div>
91
+ </div>
92
+ }
93
+ } @else {
94
+ No backups founds
95
+ }
96
+ </div>
97
+ </div>
98
+ </section>
@@ -0,0 +1,88 @@
1
+ @use '@sparkle-ui/core/styles/helpers' as *;
2
+
3
+ :host {
4
+ display: block;
5
+ }
6
+
7
+ section {
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: p2r(32);
11
+ padding: p2r(32 0);
12
+ }
13
+
14
+ header {
15
+ display: grid;
16
+ grid-template-columns: 1fr 1fr;
17
+ padding: p2r(0 32);
18
+ gap: p2r(16);
19
+ }
20
+
21
+ spk-card {
22
+ display: flex;
23
+ align-items: center;
24
+ gap: p2r(16);
25
+
26
+ .text {
27
+ flex: 1 0;
28
+ display: flex;
29
+ flex-direction: column;
30
+ gap: p2r(4);
31
+ }
32
+ }
33
+
34
+ .content {
35
+ display: flex;
36
+ flex-direction: column;
37
+ padding: 0 p2r(32);
38
+ gap: p2r(16);
39
+ }
40
+
41
+ .backups {
42
+ display: flex;
43
+ flex-direction: column;
44
+ gap: p2r(8);
45
+ }
46
+
47
+ .backup {
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: space-between;
51
+ padding: p2r(32);
52
+ gap: p2r(16);
53
+ border: 1px solid var(--base-level-40);
54
+ border-radius: var(--shape-3);
55
+ box-shadow: var(--shadow-1);
56
+
57
+ .text {
58
+ display: flex;
59
+ flex-direction: column;
60
+ gap: p2r(8);
61
+ width: 100%;
62
+ max-width: 40%;
63
+
64
+ h3 {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: p2r(8);
68
+
69
+ spk-icon {
70
+ color: var(--base-level-60);
71
+ }
72
+ }
73
+
74
+ p {
75
+ font: var(--paragraph-30);
76
+ color: var(--base-level-60);
77
+
78
+ span {
79
+ color: var(--base-level-70);
80
+ }
81
+ }
82
+ }
83
+ }
84
+
85
+ .actions {
86
+ display: flex;
87
+ gap: p2r(8);
88
+ }
@@ -0,0 +1,54 @@
1
+ import { DatePipe, JsonPipe } from '@angular/common';
2
+ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
3
+ import { RouterLink } from '@angular/router';
4
+ import {
5
+ SparkleButtonComponent,
6
+ SparkleCardComponent,
7
+ SparkleChipComponent,
8
+ SparkleIconComponent,
9
+ } from '@sparkle-ui/core';
10
+ import StatusBarComponent from '../core/components/status-bar/status-bar.component';
11
+ import { DurationFormatPipe } from '../core/pipes/duration.pipe';
12
+ import { BackupsState } from '../core/states/backups.state';
13
+
14
+ @Component({
15
+ selector: 'app-home',
16
+ standalone: true,
17
+ imports: [
18
+ RouterLink,
19
+
20
+ StatusBarComponent,
21
+
22
+ SparkleCardComponent,
23
+ SparkleButtonComponent,
24
+ SparkleIconComponent,
25
+ SparkleChipComponent,
26
+
27
+ JsonPipe,
28
+ DatePipe,
29
+ DurationFormatPipe,
30
+ ],
31
+ templateUrl: './home.component.html',
32
+ styleUrl: './home.component.scss',
33
+ changeDetection: ChangeDetectionStrategy.OnPush,
34
+ })
35
+ export default class HomeComponent {
36
+ #backupsState = inject(BackupsState);
37
+
38
+ backups = this.#backupsState.backups;
39
+ backupsLoading = this.#backupsState.backupsLoading;
40
+
41
+ ngOnInit() {
42
+ this.#backupsState.getBackups(true);
43
+ }
44
+
45
+ startBackup(id: string) {
46
+ this.#backupsState.startBackup(id);
47
+ }
48
+
49
+ deleteBackup(id: string | null | undefined) {
50
+ if (!id) return;
51
+
52
+ this.#backupsState.deleteBackup(id);
53
+ }
54
+ }
@@ -0,0 +1,64 @@
1
+ <spk-sidenav>
2
+ <div sidenav>
3
+ <header>
4
+ <app-logo />
5
+
6
+ <button spk-button class="icon small" (click)="toggleBodyClass()">
7
+ @if (isDarkMode()) {
8
+ <spk-icon>sun</spk-icon>
9
+ } @else {
10
+ <spk-icon>moon</spk-icon>
11
+ }
12
+ </button>
13
+ </header>
14
+
15
+ <spk-list class="base-level-20 primary">
16
+ <button spk-action-item routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
17
+ <spk-icon>house</spk-icon>
18
+ Home
19
+ </button>
20
+
21
+ <button spk-action-item routerLink="/add-backup" routerLinkActive="active">
22
+ <spk-icon>database</spk-icon>
23
+ Add backup
24
+ </button>
25
+
26
+ <button spk-action-item routerLink="/restore" routerLinkActive="active">
27
+ <spk-icon>clock-clockwise</spk-icon>
28
+ Restore
29
+ </button>
30
+
31
+ <button spk-action-item routerLink="/settings" routerLinkActive="active">
32
+ <spk-icon>gear</spk-icon>
33
+ Settings
34
+ </button>
35
+ </spk-list>
36
+
37
+ <div class="spacer"></div>
38
+
39
+ <spk-list class="base-level-20 primary">
40
+ <!-- <button spk-action-item (click)="">Refresh login</button> -->
41
+
42
+ <button spk-action-item routerLink="/about" routerLinkActive="active">
43
+ <spk-icon>info</spk-icon>
44
+ About duplicati
45
+ </button>
46
+
47
+ <button spk-action-item>
48
+ <spk-icon>files</spk-icon>
49
+ Documentation
50
+
51
+ <spk-icon suffix>arrow-square-out</spk-icon>
52
+ </button>
53
+
54
+ <button spk-action-item>
55
+ <spk-icon>chats-circle</spk-icon>
56
+ Visit forum
57
+
58
+ <spk-icon suffix>arrow-square-out</spk-icon>
59
+ </button>
60
+ </spk-list>
61
+ </div>
62
+
63
+ <router-outlet />
64
+ </spk-sidenav>
@@ -0,0 +1,24 @@
1
+ :host {
2
+ height: 100vh;
3
+ width: 100%;
4
+ }
5
+
6
+ header {
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: space-between;
10
+ padding: 20px;
11
+ gap: 12px;
12
+ }
13
+
14
+ h3[title] {
15
+ display: flex;
16
+ align-items: center;
17
+ justify-content: space-between;
18
+ }
19
+
20
+ div[sidenav] {
21
+ display: flex;
22
+ flex-direction: column;
23
+ height: 100vh;
24
+ }
@@ -0,0 +1,51 @@
1
+ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
2
+ import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
3
+ import {
4
+ SparkleButtonComponent,
5
+ SparkleIconComponent,
6
+ SparkleListComponent,
7
+ SparkleSidenavComponent,
8
+ } from '@sparkle-ui/core';
9
+ import LogoComponent from '../core/components/logo/logo.component';
10
+ import { BackupsState } from '../core/states/backups.state';
11
+ import { SysinfoState } from '../core/states/sysinfo.state';
12
+ import { TasksState } from '../core/states/tasks.state';
13
+ import { LayoutState } from './layout.state';
14
+
15
+ @Component({
16
+ selector: 'app-layout',
17
+ standalone: true,
18
+ imports: [
19
+ RouterOutlet,
20
+ RouterLink,
21
+ RouterLinkActive,
22
+
23
+ SparkleSidenavComponent,
24
+ SparkleIconComponent,
25
+ SparkleListComponent,
26
+ SparkleButtonComponent,
27
+
28
+ LogoComponent,
29
+ ],
30
+ templateUrl: './layout.component.html',
31
+ styleUrl: './layout.component.scss',
32
+ changeDetection: ChangeDetectionStrategy.OnPush,
33
+ })
34
+ export default class LayoutComponent {
35
+ #layoutState = inject(LayoutState);
36
+ #backupsState = inject(BackupsState);
37
+ #taskState = inject(TasksState);
38
+ #sysinfoState = inject(SysinfoState);
39
+
40
+ isDarkMode = this.#layoutState.isDarkMode;
41
+
42
+ ngOnInit() {
43
+ this.#backupsState.getBackups();
44
+ this.#sysinfoState.preloadSystemInfo();
45
+ // this.#taskState.getTasks();
46
+ }
47
+
48
+ toggleBodyClass() {
49
+ this.#layoutState.toggleBodyClass();
50
+ }
51
+ }
@@ -0,0 +1,70 @@
1
+ import { Injectable, effect, inject, signal } from '@angular/core';
2
+ import { LOCALSTORAGE } from '../core/services/localstorage.token';
3
+
4
+ declare global {
5
+ interface Window {
6
+ customData: string;
7
+ }
8
+ }
9
+
10
+ @Injectable({
11
+ providedIn: 'root',
12
+ })
13
+ export class LayoutState {
14
+ #ls = inject(LOCALSTORAGE);
15
+ #storedDarkMode = this.#ls.getItemParsed<boolean>('darkTheme', true);
16
+ #isDarkMode = signal(false);
17
+ #isMobile = signal(window?.innerWidth <= 768);
18
+ #isNavOpen = signal(false);
19
+
20
+ isDarkMode = this.#isDarkMode.asReadonly();
21
+ isMobile = this.#isMobile.asReadonly();
22
+ isNavOpen = this.#isNavOpen.asReadonly();
23
+
24
+ constructor() {
25
+ const prefersDarkMode = window.matchMedia('(prefers-color-scheme:dark)').matches;
26
+
27
+ if (prefersDarkMode && this.#storedDarkMode) {
28
+ this.setDarkMode();
29
+ }
30
+
31
+ effect(() => {
32
+ if (this.#isDarkMode()) {
33
+ document.body.classList.add('dark');
34
+ } else {
35
+ document.body.classList.remove('dark');
36
+ }
37
+ });
38
+ }
39
+
40
+ ngOnInit() {
41
+ window.addEventListener('resize', this.#resizeHandler);
42
+ }
43
+
44
+ toggleNav() {
45
+ this.#isNavOpen.set(!this.isNavOpen());
46
+ }
47
+
48
+ toggleBodyClass() {
49
+ this.#ls.setItemParsed('darkTheme', !this.isDarkMode(), true);
50
+ this.#isDarkMode.set(!this.isDarkMode());
51
+ }
52
+
53
+ setDarkMode() {
54
+ this.#ls.setItemParsed('darkTheme', true, true);
55
+ this.#isDarkMode.set(true);
56
+ }
57
+
58
+ setLightMode() {
59
+ this.#ls.setItemParsed('darkTheme', false, true);
60
+ this.#isDarkMode.set(false);
61
+ }
62
+
63
+ #resizeHandler() {
64
+ this.#isMobile.set(window.innerWidth <= 768);
65
+ }
66
+
67
+ ngOnDestroy() {
68
+ window.removeEventListener('resize', this.#resizeHandler);
69
+ }
70
+ }
@@ -0,0 +1,12 @@
1
+ <form [formGroup]="loginForm" (submit)="submit()">
2
+ <div class="login-row">
3
+ <spk-form-field>
4
+ <input placeholder="Enter your password" tabindex="1" formControlName="pass" />
5
+ </spk-form-field>
6
+
7
+ <button spk-button class="raised primary" type="submit">
8
+ Login
9
+ <spk-icon>arrow-right</spk-icon>
10
+ </button>
11
+ </div>
12
+ </form>
@@ -0,0 +1,25 @@
1
+ @use '@sparkle-ui/core/styles/helpers' as *;
2
+
3
+ :host {
4
+ display: block;
5
+ }
6
+
7
+ form {
8
+ width: 100vw;
9
+ min-height: 100vh;
10
+ display: flex;
11
+ flex-direction: column;
12
+ align-items: center;
13
+ justify-content: center;
14
+ gap: p2r(24);
15
+ }
16
+
17
+ h3 {
18
+ font: var(--title-30);
19
+ }
20
+
21
+ .login-row {
22
+ display: flex;
23
+ align-items: flex-end;
24
+ gap: p2r(8);
25
+ }
@@ -0,0 +1,34 @@
1
+ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
2
+ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
3
+ import { Router } from '@angular/router';
4
+ import { SparkleButtonComponent, SparkleFormFieldComponent, SparkleIconComponent } from '@sparkle-ui/core';
5
+ import { AppAuthState } from '../core/states/app-auth.state';
6
+
7
+ const fb = new FormBuilder();
8
+
9
+ @Component({
10
+ selector: 'app-login',
11
+ standalone: true,
12
+ imports: [ReactiveFormsModule, SparkleFormFieldComponent, SparkleIconComponent, SparkleButtonComponent],
13
+ templateUrl: './login.component.html',
14
+ styleUrl: './login.component.scss',
15
+ changeDetection: ChangeDetectionStrategy.OnPush,
16
+ })
17
+ export default class LoginComponent {
18
+ #router = inject(Router);
19
+ #auth = inject(AppAuthState);
20
+
21
+ loginForm = fb.group({
22
+ pass: fb.control<string>('', Validators.required),
23
+ });
24
+
25
+ submit() {
26
+ if (!this.loginForm.value.pass) {
27
+ return;
28
+ }
29
+
30
+ this.#auth.login(this.loginForm.value.pass!).subscribe({
31
+ next: () => this.#router.navigate(['/']),
32
+ });
33
+ }
34
+ }
@@ -0,0 +1 @@
1
+ <p>logout works!</p>,
@@ -0,0 +1,3 @@
1
+ :host {
2
+ display: block;
3
+ }