@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.
- package/.dockerignore +13 -0
- package/.github/workflows/npm-publish.yml +59 -0
- package/.prettierignore +13 -0
- package/.prettierrc +12 -0
- package/.vscode/extensions.json +4 -0
- package/.vscode/launch.json +13 -0
- package/.vscode/settings.json +8 -0
- package/.vscode/tasks.json +24 -0
- package/Dockerfile +38 -0
- package/LICENSE +21 -0
- package/README.md +27 -0
- package/angular.json +98 -0
- package/bun.lockb +0 -0
- package/openapi-ts.config.ts +13 -0
- package/package.json +46 -0
- package/scripts/generate-package-json.ts +15 -0
- package/src/app/about/about.component.html +15 -0
- package/src/app/about/about.component.scss +19 -0
- package/src/app/about/about.component.ts +13 -0
- package/src/app/about/changelog/changelog.component.html +3 -0
- package/src/app/about/changelog/changelog.component.scss +7 -0
- package/src/app/about/changelog/changelog.component.ts +18 -0
- package/src/app/about/general/general.component.html +9 -0
- package/src/app/about/general/general.component.scss +23 -0
- package/src/app/about/general/general.component.ts +30 -0
- package/src/app/about/libraries/libraries.component.html +3 -0
- package/src/app/about/libraries/libraries.component.scss +7 -0
- package/src/app/about/libraries/libraries.component.ts +18 -0
- package/src/app/about/logs/logs.component.html +5 -0
- package/src/app/about/logs/logs.component.scss +7 -0
- package/src/app/about/logs/logs.component.ts +18 -0
- package/src/app/about/system-info/system-info.component.html +3 -0
- package/src/app/about/system-info/system-info.component.scss +7 -0
- package/src/app/about/system-info/system-info.component.ts +17 -0
- package/src/app/add-backup/add-backup.component.html +29 -0
- package/src/app/add-backup/add-backup.component.scss +38 -0
- package/src/app/add-backup/add-backup.component.ts +14 -0
- package/src/app/app.component.ts +35 -0
- package/src/app/app.config.ts +23 -0
- package/src/app/app.routes.ts +121 -0
- package/src/app/core/components/file-tree/file-tree.component.html +87 -0
- package/src/app/core/components/file-tree/file-tree.component.scss +80 -0
- package/src/app/core/components/file-tree/file-tree.component.ts +485 -0
- package/src/app/core/components/logo/logo.component.html +22 -0
- package/src/app/core/components/logo/logo.component.scss +12 -0
- package/src/app/core/components/logo/logo.component.ts +11 -0
- package/src/app/core/components/status-bar/status-bar.component.html +38 -0
- package/src/app/core/components/status-bar/status-bar.component.scss +27 -0
- package/src/app/core/components/status-bar/status-bar.component.ts +33 -0
- package/src/app/core/components/status-bar/status-bar.state.ts +182 -0
- package/src/app/core/components/toggle-card/toggle-card.component.html +11 -0
- package/src/app/core/components/toggle-card/toggle-card.component.scss +49 -0
- package/src/app/core/components/toggle-card/toggle-card.component.ts +28 -0
- package/src/app/core/interceptors/http.interceptor.ts +69 -0
- package/src/app/core/interceptors/websocket.interceptor.ts +66 -0
- package/src/app/core/openapi/core/ApiError.ts +21 -0
- package/src/app/core/openapi/core/ApiRequestOptions.ts +21 -0
- package/src/app/core/openapi/core/ApiResult.ts +7 -0
- package/src/app/core/openapi/core/OpenAPI.ts +55 -0
- package/src/app/core/openapi/core/request.ts +337 -0
- package/src/app/core/openapi/index.ts +6 -0
- package/src/app/core/openapi/schemas.gen.ts +3611 -0
- package/src/app/core/openapi/services.gen.ts +1460 -0
- package/src/app/core/openapi/types.gen.ts +1510 -0
- package/src/app/core/pipes/duration.pipe.ts +27 -0
- package/src/app/core/pipes/relative-time.pipe.ts +14 -0
- package/src/app/core/providers/dayjs.ts +16 -0
- package/src/app/core/services/dict.en.ts +0 -0
- package/src/app/core/services/dictionary.service.ts +12 -0
- package/src/app/core/services/localstorage.token.ts +82 -0
- package/src/app/core/services/password-generator.service.ts +39 -0
- package/src/app/core/services/relay-websocket.service.ts +268 -0
- package/src/app/core/services/timespan-literals.service.ts +25 -0
- package/src/app/core/states/app-auth.state.ts +87 -0
- package/src/app/core/states/backups.state.ts +71 -0
- package/src/app/core/states/relayconfig.state.ts +23 -0
- package/src/app/core/states/sysinfo.state.ts +31 -0
- package/src/app/core/states/tasks.state.ts +34 -0
- package/src/app/core/types/subscribed.ts +7 -0
- package/src/app/core/validators/custom.validators.ts +120 -0
- package/src/app/create-edit-backup/create-edit-backup.component.html +28 -0
- package/src/app/create-edit-backup/create-edit-backup.component.scss +15 -0
- package/src/app/create-edit-backup/create-edit-backup.component.ts +33 -0
- package/src/app/create-edit-backup/create-edit-backup.state.ts +582 -0
- package/src/app/create-edit-backup/destination/destination.component.html +367 -0
- package/src/app/create-edit-backup/destination/destination.component.scss +85 -0
- package/src/app/create-edit-backup/destination/destination.component.ts +249 -0
- package/src/app/create-edit-backup/destination/destination.config.ts +103 -0
- package/src/app/create-edit-backup/destination/destination.mapper.ts +177 -0
- package/src/app/create-edit-backup/general/general.component.html +108 -0
- package/src/app/create-edit-backup/general/general.component.scss +34 -0
- package/src/app/create-edit-backup/general/general.component.ts +136 -0
- package/src/app/create-edit-backup/options/options.component.html +95 -0
- package/src/app/create-edit-backup/options/options.component.scss +42 -0
- package/src/app/create-edit-backup/options/options.component.ts +113 -0
- package/src/app/create-edit-backup/schedule/schedule.component.html +102 -0
- package/src/app/create-edit-backup/schedule/schedule.component.scss +38 -0
- package/src/app/create-edit-backup/schedule/schedule.component.ts +137 -0
- package/src/app/create-edit-backup/source-data/source-data.component.html +93 -0
- package/src/app/create-edit-backup/source-data/source-data.component.scss +41 -0
- package/src/app/create-edit-backup/source-data/source-data.component.ts +114 -0
- package/src/app/home/backup.const.ts +167 -0
- package/src/app/home/home.component.html +98 -0
- package/src/app/home/home.component.scss +88 -0
- package/src/app/home/home.component.ts +54 -0
- package/src/app/layout/layout.component.html +64 -0
- package/src/app/layout/layout.component.scss +24 -0
- package/src/app/layout/layout.component.ts +51 -0
- package/src/app/layout/layout.state.ts +70 -0
- package/src/app/login/login.component.html +12 -0
- package/src/app/login/login.component.scss +25 -0
- package/src/app/login/login.component.ts +34 -0
- package/src/app/logout/logout.component.html +1 -0
- package/src/app/logout/logout.component.scss +3 -0
- package/src/app/logout/logout.component.ts +18 -0
- package/src/app/restore/restore.component.html +56 -0
- package/src/app/restore/restore.component.scss +37 -0
- package/src/app/restore/restore.component.ts +55 -0
- package/src/app/restore-flow/options/options.component.html +57 -0
- package/src/app/restore-flow/options/options.component.scss +16 -0
- package/src/app/restore-flow/options/options.component.ts +58 -0
- package/src/app/restore-flow/restore-flow.component.html +14 -0
- package/src/app/restore-flow/restore-flow.component.scss +15 -0
- package/src/app/restore-flow/restore-flow.component.ts +23 -0
- package/src/app/restore-flow/restore-flow.state.ts +120 -0
- package/src/app/restore-flow/select-files/select-files.component.html +42 -0
- package/src/app/restore-flow/select-files/select-files.component.scss +16 -0
- package/src/app/restore-flow/select-files/select-files.component.ts +129 -0
- package/src/app/settings/settings.component.html +256 -0
- package/src/app/settings/settings.component.scss +106 -0
- package/src/app/settings/settings.component.ts +475 -0
- package/src/assets/duplicati-logo.png +0 -0
- package/src/assets/duplicati-logo.svg +10 -0
- package/src/assets/favicon.ico +0 -0
- package/src/assets/images/backup.png +0 -0
- package/src/assets/images/dark-theme.png +0 -0
- package/src/assets/images/folder.png +0 -0
- package/src/assets/images/light-theme.png +0 -0
- package/src/assets/images/new-backup.png +0 -0
- package/src/assets/images/restore.png +0 -0
- package/src/assets/images/system-theme.png +0 -0
- package/src/assets/spk.css +33 -0
- package/src/assets/spk.woff2 +0 -0
- package/src/environments/environment-token.ts +4 -0
- package/src/environments/environment.prod.ts +6 -0
- package/src/environments/environment.ts +6 -0
- package/src/index.html +15 -0
- package/src/main.ts +6 -0
- package/src/proxy.conf.mjs +6 -0
- package/src/styles.scss +41 -0
- package/tsconfig.app.json +15 -0
- 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>,
|