@duplicati/ngclient 0.0.1 → 0.0.6
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/chunk-22HCWJS5.js +1 -0
- package/chunk-24PLBY6C.js +1 -0
- package/chunk-3MM4GLBS.js +1 -0
- package/chunk-3MTW6FJP.js +1 -0
- package/chunk-4FK75Z4C.js +1 -0
- package/chunk-4MMHXTNF.js +1 -0
- package/chunk-57M4MADC.js +1 -0
- package/chunk-6XUWDRDE.js +1 -0
- package/chunk-77NTMJCT.js +1 -0
- package/chunk-7X6SBNMU.js +1 -0
- package/chunk-A66Z6362.js +1 -0
- package/chunk-AC6VDRLC.js +1 -0
- package/chunk-BNNZKRIC.js +1 -0
- package/chunk-CKPPU3GS.js +2 -0
- package/chunk-DYANEO5K.js +1 -0
- package/chunk-GJTW7UUU.js +1 -0
- package/chunk-GMANGOGH.js +1 -0
- package/chunk-I6QIKK6M.js +1 -0
- package/chunk-IOWSWUHM.js +1 -0
- package/chunk-IQSBPGVL.js +1 -0
- package/chunk-L47VTK2X.js +1 -0
- package/chunk-LVLU6MPQ.js +1 -0
- package/chunk-N5US3SKW.js +1 -0
- package/chunk-NIMPOFJV.js +1 -0
- package/chunk-ORDBF5MR.js +8 -0
- package/chunk-PH6AAOK4.js +1 -0
- package/chunk-PY5HJ5U2.js +1 -0
- package/chunk-QQDSFTZV.js +1 -0
- package/chunk-RANWFSNY.js +1 -0
- package/chunk-RLJJFBQ2.js +1 -0
- package/chunk-S76TN27U.js +1 -0
- package/chunk-UTPCUNNU.js +1 -0
- package/chunk-VPEVWKGB.js +1 -0
- package/chunk-VVUYO7E4.js +1 -0
- package/chunk-WLI4RZYL.js +1 -0
- package/chunk-X2NZ4CJ5.js +2 -0
- package/chunk-XU7PNEZO.js +1 -0
- package/chunk-XYJYUMYL.js +1 -0
- package/chunk-YRXI477O.js +1 -0
- package/chunk-ZD3JEKWI.js +1 -0
- package/index.html +15 -0
- package/main-5KJRELCN.js +1 -0
- package/package.json +3 -40
- package/sparkle-ui-assets/fonts/InterTight-VariableFont_wght.ttf +0 -0
- package/sparkle-ui-assets/fonts/InterTight-VariableFont_wght.woff2 +0 -0
- package/styles-6W7ZCKNM.css +1 -0
- package/.dockerignore +0 -13
- package/.github/workflows/npm-publish.yml +0 -59
- package/.prettierignore +0 -13
- package/.prettierrc +0 -12
- package/.vscode/extensions.json +0 -4
- package/.vscode/launch.json +0 -13
- package/.vscode/settings.json +0 -8
- package/.vscode/tasks.json +0 -24
- package/Dockerfile +0 -38
- package/LICENSE +0 -21
- package/README.md +0 -27
- package/angular.json +0 -98
- package/bun.lockb +0 -0
- package/openapi-ts.config.ts +0 -13
- package/scripts/generate-package-json.ts +0 -15
- package/src/app/about/about.component.html +0 -15
- package/src/app/about/about.component.scss +0 -19
- package/src/app/about/about.component.ts +0 -13
- package/src/app/about/changelog/changelog.component.html +0 -3
- package/src/app/about/changelog/changelog.component.scss +0 -7
- package/src/app/about/changelog/changelog.component.ts +0 -18
- package/src/app/about/general/general.component.html +0 -9
- package/src/app/about/general/general.component.scss +0 -23
- package/src/app/about/general/general.component.ts +0 -30
- package/src/app/about/libraries/libraries.component.html +0 -3
- package/src/app/about/libraries/libraries.component.scss +0 -7
- package/src/app/about/libraries/libraries.component.ts +0 -18
- package/src/app/about/logs/logs.component.html +0 -5
- package/src/app/about/logs/logs.component.scss +0 -7
- package/src/app/about/logs/logs.component.ts +0 -18
- package/src/app/about/system-info/system-info.component.html +0 -3
- package/src/app/about/system-info/system-info.component.scss +0 -7
- package/src/app/about/system-info/system-info.component.ts +0 -17
- package/src/app/add-backup/add-backup.component.html +0 -29
- package/src/app/add-backup/add-backup.component.scss +0 -38
- package/src/app/add-backup/add-backup.component.ts +0 -14
- package/src/app/app.component.ts +0 -35
- package/src/app/app.config.ts +0 -23
- package/src/app/app.routes.ts +0 -121
- package/src/app/core/components/file-tree/file-tree.component.html +0 -87
- package/src/app/core/components/file-tree/file-tree.component.scss +0 -80
- package/src/app/core/components/file-tree/file-tree.component.ts +0 -485
- package/src/app/core/components/logo/logo.component.html +0 -22
- package/src/app/core/components/logo/logo.component.scss +0 -12
- package/src/app/core/components/logo/logo.component.ts +0 -11
- package/src/app/core/components/status-bar/status-bar.component.html +0 -38
- package/src/app/core/components/status-bar/status-bar.component.scss +0 -27
- package/src/app/core/components/status-bar/status-bar.component.ts +0 -33
- package/src/app/core/components/status-bar/status-bar.state.ts +0 -182
- package/src/app/core/components/toggle-card/toggle-card.component.html +0 -11
- package/src/app/core/components/toggle-card/toggle-card.component.scss +0 -49
- package/src/app/core/components/toggle-card/toggle-card.component.ts +0 -28
- package/src/app/core/interceptors/http.interceptor.ts +0 -69
- package/src/app/core/interceptors/websocket.interceptor.ts +0 -66
- package/src/app/core/openapi/core/ApiError.ts +0 -21
- package/src/app/core/openapi/core/ApiRequestOptions.ts +0 -21
- package/src/app/core/openapi/core/ApiResult.ts +0 -7
- package/src/app/core/openapi/core/OpenAPI.ts +0 -55
- package/src/app/core/openapi/core/request.ts +0 -337
- package/src/app/core/openapi/index.ts +0 -6
- package/src/app/core/openapi/schemas.gen.ts +0 -3611
- package/src/app/core/openapi/services.gen.ts +0 -1460
- package/src/app/core/openapi/types.gen.ts +0 -1510
- package/src/app/core/pipes/duration.pipe.ts +0 -27
- package/src/app/core/pipes/relative-time.pipe.ts +0 -14
- package/src/app/core/providers/dayjs.ts +0 -16
- package/src/app/core/services/dict.en.ts +0 -0
- package/src/app/core/services/dictionary.service.ts +0 -12
- package/src/app/core/services/localstorage.token.ts +0 -82
- package/src/app/core/services/password-generator.service.ts +0 -39
- package/src/app/core/services/relay-websocket.service.ts +0 -268
- package/src/app/core/services/timespan-literals.service.ts +0 -25
- package/src/app/core/states/app-auth.state.ts +0 -87
- package/src/app/core/states/backups.state.ts +0 -71
- package/src/app/core/states/relayconfig.state.ts +0 -23
- package/src/app/core/states/sysinfo.state.ts +0 -31
- package/src/app/core/states/tasks.state.ts +0 -34
- package/src/app/core/types/subscribed.ts +0 -7
- package/src/app/core/validators/custom.validators.ts +0 -120
- package/src/app/create-edit-backup/create-edit-backup.component.html +0 -28
- package/src/app/create-edit-backup/create-edit-backup.component.scss +0 -15
- package/src/app/create-edit-backup/create-edit-backup.component.ts +0 -33
- package/src/app/create-edit-backup/create-edit-backup.state.ts +0 -582
- package/src/app/create-edit-backup/destination/destination.component.html +0 -367
- package/src/app/create-edit-backup/destination/destination.component.scss +0 -85
- package/src/app/create-edit-backup/destination/destination.component.ts +0 -249
- package/src/app/create-edit-backup/destination/destination.config.ts +0 -103
- package/src/app/create-edit-backup/destination/destination.mapper.ts +0 -177
- package/src/app/create-edit-backup/general/general.component.html +0 -108
- package/src/app/create-edit-backup/general/general.component.scss +0 -34
- package/src/app/create-edit-backup/general/general.component.ts +0 -136
- package/src/app/create-edit-backup/options/options.component.html +0 -95
- package/src/app/create-edit-backup/options/options.component.scss +0 -42
- package/src/app/create-edit-backup/options/options.component.ts +0 -113
- package/src/app/create-edit-backup/schedule/schedule.component.html +0 -102
- package/src/app/create-edit-backup/schedule/schedule.component.scss +0 -38
- package/src/app/create-edit-backup/schedule/schedule.component.ts +0 -137
- package/src/app/create-edit-backup/source-data/source-data.component.html +0 -93
- package/src/app/create-edit-backup/source-data/source-data.component.scss +0 -41
- package/src/app/create-edit-backup/source-data/source-data.component.ts +0 -114
- package/src/app/home/backup.const.ts +0 -167
- package/src/app/home/home.component.html +0 -98
- package/src/app/home/home.component.scss +0 -88
- package/src/app/home/home.component.ts +0 -54
- package/src/app/layout/layout.component.html +0 -64
- package/src/app/layout/layout.component.scss +0 -24
- package/src/app/layout/layout.component.ts +0 -51
- package/src/app/layout/layout.state.ts +0 -70
- package/src/app/login/login.component.html +0 -12
- package/src/app/login/login.component.scss +0 -25
- package/src/app/login/login.component.ts +0 -34
- package/src/app/logout/logout.component.html +0 -1
- package/src/app/logout/logout.component.scss +0 -3
- package/src/app/logout/logout.component.ts +0 -18
- package/src/app/restore/restore.component.html +0 -56
- package/src/app/restore/restore.component.scss +0 -37
- package/src/app/restore/restore.component.ts +0 -55
- package/src/app/restore-flow/options/options.component.html +0 -57
- package/src/app/restore-flow/options/options.component.scss +0 -16
- package/src/app/restore-flow/options/options.component.ts +0 -58
- package/src/app/restore-flow/restore-flow.component.html +0 -14
- package/src/app/restore-flow/restore-flow.component.scss +0 -15
- package/src/app/restore-flow/restore-flow.component.ts +0 -23
- package/src/app/restore-flow/restore-flow.state.ts +0 -120
- package/src/app/restore-flow/select-files/select-files.component.html +0 -42
- package/src/app/restore-flow/select-files/select-files.component.scss +0 -16
- package/src/app/restore-flow/select-files/select-files.component.ts +0 -129
- package/src/app/settings/settings.component.html +0 -256
- package/src/app/settings/settings.component.scss +0 -106
- package/src/app/settings/settings.component.ts +0 -475
- package/src/environments/environment-token.ts +0 -4
- package/src/environments/environment.prod.ts +0 -6
- package/src/environments/environment.ts +0 -6
- package/src/index.html +0 -15
- package/src/main.ts +0 -6
- package/src/proxy.conf.mjs +0 -6
- package/src/styles.scss +0 -41
- package/tsconfig.app.json +0 -15
- package/tsconfig.json +0 -30
- /package/{src/assets → assets}/duplicati-logo.png +0 -0
- /package/{src/assets → assets}/duplicati-logo.svg +0 -0
- /package/{src/assets → assets}/favicon.ico +0 -0
- /package/{src/assets → assets}/images/backup.png +0 -0
- /package/{src/assets → assets}/images/dark-theme.png +0 -0
- /package/{src/assets → assets}/images/folder.png +0 -0
- /package/{src/assets → assets}/images/light-theme.png +0 -0
- /package/{src/assets → assets}/images/new-backup.png +0 -0
- /package/{src/assets → assets}/images/restore.png +0 -0
- /package/{src/assets → assets}/images/system-theme.png +0 -0
- /package/{src/assets → assets}/spk.css +0 -0
- /package/{src/assets → assets}/spk.woff2 +0 -0
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { FormBuilder } from '@angular/forms';
|
|
2
|
-
import { ArgumentType, ICommandLineArgument } from '../../core/openapi';
|
|
3
|
-
|
|
4
|
-
export type FormView = {
|
|
5
|
-
name: string;
|
|
6
|
-
type: ArgumentType | 'file-tree';
|
|
7
|
-
shortDescription?: string;
|
|
8
|
-
longDescription?: string;
|
|
9
|
-
options?: ICommandLineArgument['ValidValues'];
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export type CustomFormView = FormView & {
|
|
13
|
-
formElement: any;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export type DestinationConfig = {
|
|
17
|
-
[key: string]: {
|
|
18
|
-
oauthField?: string;
|
|
19
|
-
customFields?: {
|
|
20
|
-
[key: string]: CustomFormView;
|
|
21
|
-
};
|
|
22
|
-
dynamicFields?: string[];
|
|
23
|
-
advancedFields?: string[];
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const fb = new FormBuilder();
|
|
28
|
-
|
|
29
|
-
export const DESTINATION_CONFIG: DestinationConfig = {
|
|
30
|
-
file: {
|
|
31
|
-
customFields: {
|
|
32
|
-
path: {
|
|
33
|
-
type: 'file-tree',
|
|
34
|
-
name: 'path',
|
|
35
|
-
shortDescription: 'File path',
|
|
36
|
-
longDescription: 'The path to store the backup',
|
|
37
|
-
formElement: (defaultValue?: any) => fb.control<string>(defaultValue ?? ''),
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
ssh: {
|
|
42
|
-
customFields: {
|
|
43
|
-
server: {
|
|
44
|
-
type: 'String',
|
|
45
|
-
name: 'server',
|
|
46
|
-
shortDescription: 'Server',
|
|
47
|
-
longDescription: 'The server to connect to',
|
|
48
|
-
formElement: (defaultValue?: any) => fb.control<string>(defaultValue ?? ''),
|
|
49
|
-
},
|
|
50
|
-
port: {
|
|
51
|
-
type: 'Integer',
|
|
52
|
-
name: 'port',
|
|
53
|
-
shortDescription: 'Port',
|
|
54
|
-
longDescription: 'The port to connect to',
|
|
55
|
-
formElement: (defaultValue?: any) => fb.control<string>(defaultValue ?? ''),
|
|
56
|
-
},
|
|
57
|
-
path: {
|
|
58
|
-
type: 'Path',
|
|
59
|
-
name: 'path',
|
|
60
|
-
shortDescription: 'File path on the server',
|
|
61
|
-
longDescription: 'File path on the server',
|
|
62
|
-
formElement: (defaultValue?: any) => fb.control<string>(defaultValue ?? ''),
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
dynamicFields: ['auth-username'],
|
|
66
|
-
},
|
|
67
|
-
s3: {
|
|
68
|
-
customFields: {
|
|
69
|
-
path: {
|
|
70
|
-
type: 'Path',
|
|
71
|
-
name: 'path',
|
|
72
|
-
shortDescription: 'File path on the server',
|
|
73
|
-
longDescription: 'File path on the server',
|
|
74
|
-
formElement: (defaultValue?: any) => fb.control<string>(defaultValue ?? ''),
|
|
75
|
-
},
|
|
76
|
-
bucket: {
|
|
77
|
-
type: 'String',
|
|
78
|
-
name: 'bucket',
|
|
79
|
-
shortDescription: 'Bucket name',
|
|
80
|
-
longDescription: 'Bucket name',
|
|
81
|
-
formElement: (defaultValue?: any) => fb.control<string>(defaultValue ?? ''),
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
dynamicFields: ['use-ssl', 's3-server-name', 'auth-username', 'auth-password'],
|
|
85
|
-
},
|
|
86
|
-
gcs: {
|
|
87
|
-
dynamicFields: ['gcs-location', 'gcs-storage-class', 'authid'],
|
|
88
|
-
},
|
|
89
|
-
googledrive: {
|
|
90
|
-
oauthField: 'authid',
|
|
91
|
-
|
|
92
|
-
customFields: {
|
|
93
|
-
path: {
|
|
94
|
-
type: 'Path',
|
|
95
|
-
name: 'path',
|
|
96
|
-
shortDescription: 'File path on the server',
|
|
97
|
-
longDescription: 'File path on the server',
|
|
98
|
-
formElement: (defaultValue?: any) => fb.control<string>(defaultValue ?? ''),
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
dynamicFields: ['authid'],
|
|
102
|
-
},
|
|
103
|
-
};
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import { FormControl, FormGroup } from '@angular/forms';
|
|
2
|
-
import { DestinationFormGroupValue } from './destination.component';
|
|
3
|
-
import { DESTINATION_CONFIG } from './destination.config';
|
|
4
|
-
|
|
5
|
-
type DestinationType = keyof typeof mappings | string;
|
|
6
|
-
|
|
7
|
-
export type DestinationFormGroup = FormGroup<{
|
|
8
|
-
destinationType: FormControl<string>;
|
|
9
|
-
custom: FormGroup<any>;
|
|
10
|
-
dynamic: FormGroup<any>;
|
|
11
|
-
advanced: FormGroup<any>;
|
|
12
|
-
}>;
|
|
13
|
-
|
|
14
|
-
export type ValueOfDestinationFormGroup = DestinationFormGroup['value'];
|
|
15
|
-
|
|
16
|
-
function handleSearchParams(destinationType: string, urlObj: URL) {
|
|
17
|
-
const advanced: { [key: string]: any } = {};
|
|
18
|
-
const dynamic: { [key: string]: any } = {};
|
|
19
|
-
|
|
20
|
-
urlObj.searchParams.forEach((value, key) => {
|
|
21
|
-
if (DESTINATION_CONFIG[destinationType].dynamicFields?.includes(key)) {
|
|
22
|
-
dynamic[key] = decodeURIComponent(value);
|
|
23
|
-
} else {
|
|
24
|
-
advanced[key] = decodeURIComponent(value);
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
advanced,
|
|
30
|
-
dynamic,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function toSearchParams(arr: [string, string | number | unknown][]) {
|
|
35
|
-
const searchParamString = arr
|
|
36
|
-
.map(([key, value]) => {
|
|
37
|
-
return `${key}=${value ? encodeURIComponent((value as string | number).toString()) : ''}`;
|
|
38
|
-
})
|
|
39
|
-
.join('&');
|
|
40
|
-
|
|
41
|
-
return searchParamString ? '?' + searchParamString : '';
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const mappings = {
|
|
45
|
-
file: {
|
|
46
|
-
to: (fields: ValueOfDestinationFormGroup): string => {
|
|
47
|
-
const urlParams = toSearchParams([...Object.entries(fields.advanced), ...Object.entries(fields.dynamic)]);
|
|
48
|
-
|
|
49
|
-
return `${fields.destinationType}://${fields.custom.path}${urlParams}`;
|
|
50
|
-
},
|
|
51
|
-
from: (destinationType: string, urlObj: URL, plainPath: string) => {
|
|
52
|
-
const hasLeadingSlash = plainPath.indexOf('file:///') === 0;
|
|
53
|
-
const hostNameCapitalFirstLetter =
|
|
54
|
-
plainPath.split(hasLeadingSlash ? '///' : '//')[1].substring(0, 1) + urlObj.hostname.substring(1);
|
|
55
|
-
|
|
56
|
-
return <ValueOfDestinationFormGroup>{
|
|
57
|
-
destinationType,
|
|
58
|
-
custom: {
|
|
59
|
-
path: `${hasLeadingSlash ? '/' : ''}${hostNameCapitalFirstLetter}${urlObj.pathname}`,
|
|
60
|
-
},
|
|
61
|
-
...handleSearchParams(destinationType, urlObj),
|
|
62
|
-
};
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
ssh: {
|
|
66
|
-
to: (fields: any): string => {
|
|
67
|
-
const urlParams = toSearchParams([...Object.entries(fields.advanced), ...Object.entries(fields.dynamic)]);
|
|
68
|
-
|
|
69
|
-
return `${fields.destinationType}://${fields.custom.server}${fields.custom.port ? ':' + fields.custom.port : ''}${fields.custom.path.startsWith('/') ? fields.custom.path : '/' + fields.custom.path}${urlParams}`;
|
|
70
|
-
},
|
|
71
|
-
from: (destinationType: string, urlObj: URL, plainPath: string) => {
|
|
72
|
-
return <ValueOfDestinationFormGroup>{
|
|
73
|
-
destinationType,
|
|
74
|
-
custom: {
|
|
75
|
-
server: urlObj.hostname,
|
|
76
|
-
port: urlObj.port,
|
|
77
|
-
path: urlObj.pathname,
|
|
78
|
-
},
|
|
79
|
-
...handleSearchParams(destinationType, urlObj),
|
|
80
|
-
};
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
s3: {
|
|
84
|
-
to: (fields: any): string => {
|
|
85
|
-
const urlParams = toSearchParams([...Object.entries(fields.advanced), ...Object.entries(fields.dynamic)]);
|
|
86
|
-
|
|
87
|
-
return `${fields.destinationType}://${fields.custom.bucket}${fields.custom.path}${urlParams}`;
|
|
88
|
-
},
|
|
89
|
-
from: (destinationType: string, urlObj: URL, plainPath: string) => {
|
|
90
|
-
return <ValueOfDestinationFormGroup>{
|
|
91
|
-
destinationType,
|
|
92
|
-
custom: {
|
|
93
|
-
path: urlObj.pathname,
|
|
94
|
-
bucket: urlObj.hostname,
|
|
95
|
-
},
|
|
96
|
-
...handleSearchParams(destinationType, urlObj),
|
|
97
|
-
};
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
gcs: {
|
|
101
|
-
to: (fields: any): string => {
|
|
102
|
-
const bucket = fields.dynamic['gcs-location'] ?? '';
|
|
103
|
-
|
|
104
|
-
delete fields.dynamic['gcs-location'];
|
|
105
|
-
|
|
106
|
-
const urlParams = toSearchParams([...Object.entries(fields.advanced), ...Object.entries(fields.dynamic)]);
|
|
107
|
-
|
|
108
|
-
return `${fields.destinationType}://${bucket}${urlParams}`;
|
|
109
|
-
},
|
|
110
|
-
from: (destinationType: string, urlObj: URL, plainPath: string) => {
|
|
111
|
-
const { advanced, dynamic } = handleSearchParams(destinationType, urlObj);
|
|
112
|
-
return <ValueOfDestinationFormGroup>{
|
|
113
|
-
destinationType,
|
|
114
|
-
advanced,
|
|
115
|
-
dynamic: {
|
|
116
|
-
...dynamic,
|
|
117
|
-
'gcs-location': urlObj.hostname + urlObj.pathname,
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
googledrive: {
|
|
123
|
-
to: (fields: any): string => {
|
|
124
|
-
const path = fields.custom.path;
|
|
125
|
-
|
|
126
|
-
return `${fields.destinationType}://${path}`;
|
|
127
|
-
},
|
|
128
|
-
from: (destinationType: string, urlObj: URL, plainPath: string) => {
|
|
129
|
-
return <ValueOfDestinationFormGroup>{
|
|
130
|
-
destinationType,
|
|
131
|
-
custom: {
|
|
132
|
-
path: plainPath,
|
|
133
|
-
},
|
|
134
|
-
...handleSearchParams(destinationType, urlObj),
|
|
135
|
-
};
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
fallbackMapper: {
|
|
140
|
-
to: (fields: any): string => {
|
|
141
|
-
return '';
|
|
142
|
-
},
|
|
143
|
-
from: (destinationType: string, urlObj: URL, plainPath: string) => {
|
|
144
|
-
return <ValueOfDestinationFormGroup>{
|
|
145
|
-
destinationType,
|
|
146
|
-
...handleSearchParams(destinationType, urlObj),
|
|
147
|
-
};
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
export function toTargetPath(fields: DestinationFormGroupValue): string {
|
|
153
|
-
const destinationType = fields.destinationType;
|
|
154
|
-
|
|
155
|
-
if (!destinationType || !mappings.hasOwnProperty(destinationType)) {
|
|
156
|
-
return ((mappings as any)['fallbackMapper'].to(fields) as string) ?? '';
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return ((mappings as any)[destinationType].to(fields) as string) ?? '';
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function fromTargetPath(targetPath: string) {
|
|
163
|
-
const canParse = URL.canParse(targetPath);
|
|
164
|
-
const destinationType = targetPath.split('://')[0];
|
|
165
|
-
const fakeProtocolPrefixed = 'http://' + targetPath.split('://')[1];
|
|
166
|
-
const urlObj = new URL(fakeProtocolPrefixed);
|
|
167
|
-
|
|
168
|
-
if (!mappings.hasOwnProperty(destinationType)) {
|
|
169
|
-
return (mappings as any)['fallbackMapper'].from(destinationType, urlObj);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (!canParse) {
|
|
173
|
-
throw new Error('Invalid target path');
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return (mappings as any)[destinationType].from(destinationType, urlObj, targetPath);
|
|
177
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
<form [formGroup]="generalForm" autocomplete="off" #formRef (submit)="next()">
|
|
2
|
-
<h3 class="title-30">General backup settings</h3>
|
|
3
|
-
|
|
4
|
-
<spk-form-field>
|
|
5
|
-
<label for="backupName">Backup name</label>
|
|
6
|
-
<input type="text" tabindex="1" placeholder="Your backup name" formControlName="name" />
|
|
7
|
-
</spk-form-field>
|
|
8
|
-
|
|
9
|
-
<spk-form-field>
|
|
10
|
-
<label for="backupDescription">Backup description</label>
|
|
11
|
-
<textarea
|
|
12
|
-
id="backupDescription"
|
|
13
|
-
tabindex="2"
|
|
14
|
-
placeholder="Your backup description"
|
|
15
|
-
formControlName="description"></textarea>
|
|
16
|
-
</spk-form-field>
|
|
17
|
-
|
|
18
|
-
@if (isNew()) {
|
|
19
|
-
<spk-select [displayValue]="displayFn(generalForm.value.encryption ?? '')">
|
|
20
|
-
<label for="encryption">Encryption</label>
|
|
21
|
-
<input type="text" id="encryption" tabindex="3" placeholder="Encryption" formControlName="encryption" />
|
|
22
|
-
|
|
23
|
-
<ng-container options>
|
|
24
|
-
@for (encryption of encryptionOptions(); track $index) {
|
|
25
|
-
<option [value]="encryption.Key">{{ encryption.DisplayName }}</option>
|
|
26
|
-
}
|
|
27
|
-
</ng-container>
|
|
28
|
-
</spk-select>
|
|
29
|
-
|
|
30
|
-
@if (encryptionFieldSignal() !== 'none') {
|
|
31
|
-
<div class="passwords">
|
|
32
|
-
<spk-form-field>
|
|
33
|
-
<label for="password">Create a password</label>
|
|
34
|
-
<input
|
|
35
|
-
[type]="showPassword() ? 'text' : 'password'"
|
|
36
|
-
id="password"
|
|
37
|
-
tabindex="4"
|
|
38
|
-
placeholder="Password"
|
|
39
|
-
formControlName="password" />
|
|
40
|
-
</spk-form-field>
|
|
41
|
-
|
|
42
|
-
<spk-form-field>
|
|
43
|
-
<input
|
|
44
|
-
[type]="showPassword() ? 'text' : 'password'"
|
|
45
|
-
id="repeatPassword"
|
|
46
|
-
tabindex="5"
|
|
47
|
-
placeholder="Repeat password"
|
|
48
|
-
formControlName="repeatPassword" />
|
|
49
|
-
</spk-form-field>
|
|
50
|
-
|
|
51
|
-
<div class="password-actions">
|
|
52
|
-
<button spk-button type="button" class="small" (click)="showPassword.set(!showPassword())">
|
|
53
|
-
@if (showPassword()) {
|
|
54
|
-
<spk-icon>eye-slash</spk-icon>
|
|
55
|
-
hide
|
|
56
|
-
} @else {
|
|
57
|
-
<spk-icon>eye</spk-icon>
|
|
58
|
-
show
|
|
59
|
-
}
|
|
60
|
-
</button>
|
|
61
|
-
|
|
62
|
-
<button spk-button type="button" class="small" (click)="generatePassword()">
|
|
63
|
-
<spk-icon>password</spk-icon>
|
|
64
|
-
Generate password
|
|
65
|
-
</button>
|
|
66
|
-
|
|
67
|
-
@if (showCopyPassword()) {
|
|
68
|
-
<button spk-button type="button" class="small copy" (click)="copyPassword()">
|
|
69
|
-
@if (copiedPassword()) {
|
|
70
|
-
<spk-icon class="spk-success">check</spk-icon>
|
|
71
|
-
} @else {
|
|
72
|
-
<spk-icon>copy</spk-icon>
|
|
73
|
-
}
|
|
74
|
-
{{ copiedPassword() ? 'Copied' : 'Copy' }} password
|
|
75
|
-
</button>
|
|
76
|
-
}
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
<div class="form-actions">
|
|
83
|
-
<button spk-button (click)="exit()">
|
|
84
|
-
<spk-icon>arrow-left</spk-icon>
|
|
85
|
-
Exit
|
|
86
|
-
</button>
|
|
87
|
-
|
|
88
|
-
<div
|
|
89
|
-
[attr.spk-tooltip-primary]="
|
|
90
|
-
(generalForm.valid && encryptionFieldSignal() === 'none') || copiedPassword()
|
|
91
|
-
? undefined
|
|
92
|
-
: 'Remember to copy the password'
|
|
93
|
-
">
|
|
94
|
-
<button
|
|
95
|
-
spk-button
|
|
96
|
-
class="raised primary"
|
|
97
|
-
type="submit"
|
|
98
|
-
[disabled]="
|
|
99
|
-
generalForm.invalid || (encryptionFieldSignal() !== 'none' && showCopyPassword() && !copiedPassword())
|
|
100
|
-
">
|
|
101
|
-
<spk-icon>arrow-right</spk-icon>
|
|
102
|
-
Continue
|
|
103
|
-
</button>
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
</form>
|
|
107
|
-
|
|
108
|
-
<!-- <pre>{{ generalForm.value | json }}</pre> -->
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
@use '@sparkle-ui/core/styles/helpers' as *;
|
|
2
|
-
|
|
3
|
-
:host {
|
|
4
|
-
display: block;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
form {
|
|
8
|
-
display: flex;
|
|
9
|
-
flex-direction: column;
|
|
10
|
-
gap: p2r(24);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
.passwords {
|
|
14
|
-
display: flex;
|
|
15
|
-
flex-direction: column;
|
|
16
|
-
gap: p2r(8);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.password-actions {
|
|
20
|
-
display: flex;
|
|
21
|
-
gap: p2r(8);
|
|
22
|
-
align-items: center;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.form-actions {
|
|
26
|
-
display: flex;
|
|
27
|
-
justify-content: space-between;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
.copy {
|
|
31
|
-
spk-icon.spk-success {
|
|
32
|
-
color: var(--success-500);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { JsonPipe } from '@angular/common';
|
|
2
|
-
import { ChangeDetectionStrategy, Component, ElementRef, inject, signal, viewChild } from '@angular/core';
|
|
3
|
-
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
4
|
-
import { ActivatedRoute, Router } from '@angular/router';
|
|
5
|
-
import {
|
|
6
|
-
SparkleButtonComponent,
|
|
7
|
-
SparkleFormFieldComponent,
|
|
8
|
-
SparkleIconComponent,
|
|
9
|
-
SparkleSelectComponent,
|
|
10
|
-
} from '@sparkle-ui/core';
|
|
11
|
-
import { IDynamicModule } from '../../core/openapi';
|
|
12
|
-
import { PasswordGeneratorService } from '../../core/services/password-generator.service';
|
|
13
|
-
import { SysinfoState } from '../../core/states/sysinfo.state';
|
|
14
|
-
import { validateWhen, watchField } from '../../core/validators/custom.validators';
|
|
15
|
-
import { CreateEditBackupState } from '../create-edit-backup.state';
|
|
16
|
-
|
|
17
|
-
const fb = new FormBuilder();
|
|
18
|
-
|
|
19
|
-
export const createGeneralForm = (
|
|
20
|
-
defaults = {
|
|
21
|
-
name: '',
|
|
22
|
-
description: '',
|
|
23
|
-
encryption: 'none',
|
|
24
|
-
password: '',
|
|
25
|
-
repeatPassword: '',
|
|
26
|
-
}
|
|
27
|
-
) => {
|
|
28
|
-
return fb.group({
|
|
29
|
-
name: fb.control<string>(defaults.name, [Validators.required]),
|
|
30
|
-
description: fb.control<string>(defaults.description),
|
|
31
|
-
encryption: fb.control<string>(defaults.encryption, [Validators.required, watchField()]),
|
|
32
|
-
password: fb.control<string>(defaults.password, [
|
|
33
|
-
validateWhen((t) => t?.value.encryption !== 'none', [Validators.required]),
|
|
34
|
-
]),
|
|
35
|
-
repeatPassword: fb.control<string>(defaults.repeatPassword, [
|
|
36
|
-
validateWhen((t) => t?.value.encryption !== 'none', [Validators.required]),
|
|
37
|
-
]),
|
|
38
|
-
});
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export const NONE_OPTION = {
|
|
42
|
-
Key: 'none',
|
|
43
|
-
DisplayName: 'None',
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
@Component({
|
|
47
|
-
selector: 'app-general',
|
|
48
|
-
standalone: true,
|
|
49
|
-
imports: [
|
|
50
|
-
ReactiveFormsModule,
|
|
51
|
-
SparkleFormFieldComponent,
|
|
52
|
-
SparkleSelectComponent,
|
|
53
|
-
SparkleButtonComponent,
|
|
54
|
-
SparkleIconComponent,
|
|
55
|
-
JsonPipe,
|
|
56
|
-
],
|
|
57
|
-
templateUrl: './general.component.html',
|
|
58
|
-
styleUrl: './general.component.scss',
|
|
59
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
60
|
-
})
|
|
61
|
-
export default class GeneralComponent {
|
|
62
|
-
#router = inject(Router);
|
|
63
|
-
#route = inject(ActivatedRoute);
|
|
64
|
-
#passwordGeneratorService = inject(PasswordGeneratorService);
|
|
65
|
-
#createEditBackupState = inject(CreateEditBackupState);
|
|
66
|
-
#sysinfo = inject(SysinfoState);
|
|
67
|
-
|
|
68
|
-
formRef = viewChild.required<ElementRef<HTMLFormElement>>('formRef');
|
|
69
|
-
generalForm = this.#createEditBackupState.generalForm;
|
|
70
|
-
encryptionFieldSignal = this.#createEditBackupState.encryptionFieldSignal;
|
|
71
|
-
encryptionOptions = this.#createEditBackupState.encryptionOptions;
|
|
72
|
-
isNew = this.#createEditBackupState.isNew;
|
|
73
|
-
|
|
74
|
-
showPassword = signal(false);
|
|
75
|
-
copiedPassword = signal(false);
|
|
76
|
-
showCopyPassword = signal(false);
|
|
77
|
-
|
|
78
|
-
ngOnInit() {
|
|
79
|
-
this.formRef().nativeElement.focus();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
exit() {
|
|
83
|
-
this.#createEditBackupState.exit();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async copyPassword() {
|
|
87
|
-
const pass = this.generalForm.controls.password.value;
|
|
88
|
-
|
|
89
|
-
await this.#copyToClipboard(pass ?? '');
|
|
90
|
-
|
|
91
|
-
this.copiedPassword.set(true);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async #copyToClipboard(text: string) {
|
|
95
|
-
try {
|
|
96
|
-
// Attempt to use the Clipboard API
|
|
97
|
-
await navigator.clipboard.writeText(text);
|
|
98
|
-
} catch (err) {
|
|
99
|
-
// Fallback to execCommand if Clipboard API fails
|
|
100
|
-
try {
|
|
101
|
-
const textArea = document.createElement('textarea');
|
|
102
|
-
textArea.value = text;
|
|
103
|
-
document.body.appendChild(textArea);
|
|
104
|
-
textArea.select();
|
|
105
|
-
document.execCommand('copy');
|
|
106
|
-
document.body.removeChild(textArea);
|
|
107
|
-
} catch (err) {
|
|
108
|
-
console.error('Failed to copy text: ', err);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
displayFn(val: IDynamicModule['Key']) {
|
|
114
|
-
const encryptionOptions = this.#sysinfo.systemInfo()?.EncryptionModules ?? [];
|
|
115
|
-
const item = [NONE_OPTION, ...encryptionOptions].find((x) => x.Key === val);
|
|
116
|
-
|
|
117
|
-
if (!item) {
|
|
118
|
-
return '';
|
|
119
|
-
}
|
|
120
|
-
return `${item.DisplayName}`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
generatePassword() {
|
|
124
|
-
this.copiedPassword.set(false);
|
|
125
|
-
|
|
126
|
-
const newPass = this.#passwordGeneratorService.generate(16);
|
|
127
|
-
this.generalForm.controls.password.setValue(newPass);
|
|
128
|
-
this.generalForm.controls.repeatPassword.setValue(newPass);
|
|
129
|
-
|
|
130
|
-
this.showCopyPassword.set(true);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
next() {
|
|
134
|
-
this.#router.navigate(['destination'], { relativeTo: this.#route.parent });
|
|
135
|
-
}
|
|
136
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
<form [formGroup]="optionsForm" #formRef (submit)="submit()">
|
|
2
|
-
<h3 class="title-30">Options</h3>
|
|
3
|
-
|
|
4
|
-
<div class="remote-volume-wrap">
|
|
5
|
-
<div class="row-inputs" formGroupName="remoteVolumeSize">
|
|
6
|
-
<spk-form-field>
|
|
7
|
-
<label for="size">Remote volume size</label>
|
|
8
|
-
<input type="number" id="size" formControlName="size" />
|
|
9
|
-
</spk-form-field>
|
|
10
|
-
|
|
11
|
-
<spk-select>
|
|
12
|
-
<input type="text" placeholder="Select an option..." formControlName="unit" />
|
|
13
|
-
|
|
14
|
-
<ng-container options>
|
|
15
|
-
@for (option of sizeOptions(); track $index) {
|
|
16
|
-
<option [value]="option">{{ option }}</option>
|
|
17
|
-
}
|
|
18
|
-
</ng-container>
|
|
19
|
-
</spk-select>
|
|
20
|
-
</div>
|
|
21
|
-
|
|
22
|
-
<p>
|
|
23
|
-
The backups will be split up into multiple files called volumes. Here you can set the maximum size of the
|
|
24
|
-
individual volume files.
|
|
25
|
-
<a href="#" class="spk-primary">Read more</a>
|
|
26
|
-
</p>
|
|
27
|
-
</div>
|
|
28
|
-
|
|
29
|
-
<spk-select>
|
|
30
|
-
<label for="size">Backup retention</label>
|
|
31
|
-
<input type="text" placeholder="Local folder or drive" formControlName="backupRetention" />
|
|
32
|
-
|
|
33
|
-
<ng-container options>
|
|
34
|
-
@for (option of rentationOptions(); track $index) {
|
|
35
|
-
<option [value]="option">{{ option }}</option>
|
|
36
|
-
}
|
|
37
|
-
</ng-container>
|
|
38
|
-
</spk-select>
|
|
39
|
-
|
|
40
|
-
<app-toggle-card [disallowToggle]="true">
|
|
41
|
-
<ng-container title>
|
|
42
|
-
Advanced options
|
|
43
|
-
|
|
44
|
-
<spk-menu>
|
|
45
|
-
<button spk-button type="button" class="outlined small">
|
|
46
|
-
Add advanced option
|
|
47
|
-
<spk-icon>plus</spk-icon>
|
|
48
|
-
</button>
|
|
49
|
-
|
|
50
|
-
<ng-container menu>
|
|
51
|
-
@for (option of nonSelectedAdvancedOptions(); track $index) {
|
|
52
|
-
<button spk-menu-item type="button" (click)="addNewOption(option)">{{ option.Name }}</button>
|
|
53
|
-
}
|
|
54
|
-
</ng-container>
|
|
55
|
-
</spk-menu>
|
|
56
|
-
</ng-container>
|
|
57
|
-
|
|
58
|
-
<ng-container formArrayName="advancedOptions">
|
|
59
|
-
@for (optionCtrl of optionsForm.controls.advancedOptions.controls; track $index) {
|
|
60
|
-
<div class="advanced-option" [formGroup]="optionCtrl">
|
|
61
|
-
<spk-form-field>
|
|
62
|
-
@if ($index === 0) {
|
|
63
|
-
<label for="name">Name</label>
|
|
64
|
-
}
|
|
65
|
-
<input type="text" id="name" formControlName="name" />
|
|
66
|
-
</spk-form-field>
|
|
67
|
-
|
|
68
|
-
<spk-form-field>
|
|
69
|
-
@if ($index === 0) {
|
|
70
|
-
<label for="value">Value</label>
|
|
71
|
-
}
|
|
72
|
-
<input type="text" id="value" formControlName="value" />
|
|
73
|
-
</spk-form-field>
|
|
74
|
-
|
|
75
|
-
<button class="icon" spk-button type="button" (click)="optionsForm.controls.advancedOptions.removeAt($index)">
|
|
76
|
-
<spk-icon>x-circle</spk-icon>
|
|
77
|
-
</button>
|
|
78
|
-
</div>
|
|
79
|
-
}
|
|
80
|
-
</ng-container>
|
|
81
|
-
</app-toggle-card>
|
|
82
|
-
|
|
83
|
-
<div class="form-actions">
|
|
84
|
-
<button spk-button type="button" (click)="goBack()">
|
|
85
|
-
<spk-icon>arrow-left</spk-icon>
|
|
86
|
-
Go back
|
|
87
|
-
</button>
|
|
88
|
-
|
|
89
|
-
<button spk-button class="raised primary" type="submit">
|
|
90
|
-
<spk-icon>arrow-right</spk-icon>
|
|
91
|
-
Submit
|
|
92
|
-
</button>
|
|
93
|
-
</div>
|
|
94
|
-
<!-- <pre>{{ optionsForm.value | json }}</pre> -->
|
|
95
|
-
</form>
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
@use '@sparkle-ui/core/styles/helpers' as *;
|
|
2
|
-
|
|
3
|
-
:host {
|
|
4
|
-
display: block;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
form {
|
|
8
|
-
display: flex;
|
|
9
|
-
flex-direction: column;
|
|
10
|
-
gap: p2r(24);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
.remote-volume-wrap {
|
|
14
|
-
display: flex;
|
|
15
|
-
flex-direction: column;
|
|
16
|
-
gap: p2r(4);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.row-inputs {
|
|
20
|
-
display: flex;
|
|
21
|
-
align-items: flex-end;
|
|
22
|
-
gap: p2r(8);
|
|
23
|
-
|
|
24
|
-
> * {
|
|
25
|
-
flex: 1 0;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
p {
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.advanced-option {
|
|
33
|
-
display: grid;
|
|
34
|
-
gap: p2r(8);
|
|
35
|
-
grid-template-columns: 1fr 1fr 40px;
|
|
36
|
-
align-items: flex-end;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.form-actions {
|
|
40
|
-
display: flex;
|
|
41
|
-
justify-content: space-between;
|
|
42
|
-
}
|