mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
refactor: domains page
This commit is contained in:
200
web/package-lock.json
generated
200
web/package-lock.json
generated
@@ -25,19 +25,19 @@
|
|||||||
"@noble/hashes": "^1.4.0",
|
"@noble/hashes": "^1.4.0",
|
||||||
"@start9labs/argon2": "^0.3.0",
|
"@start9labs/argon2": "^0.3.0",
|
||||||
"@start9labs/start-sdk": "file:../sdk/baseDist",
|
"@start9labs/start-sdk": "file:../sdk/baseDist",
|
||||||
"@taiga-ui/addon-charts": "4.44.0",
|
"@taiga-ui/addon-charts": "4.47.0",
|
||||||
"@taiga-ui/addon-commerce": "4.44.0",
|
"@taiga-ui/addon-commerce": "4.47.0",
|
||||||
"@taiga-ui/addon-mobile": "4.44.0",
|
"@taiga-ui/addon-mobile": "4.47.0",
|
||||||
"@taiga-ui/addon-table": "4.44.0",
|
"@taiga-ui/addon-table": "4.47.0",
|
||||||
"@taiga-ui/cdk": "4.44.0",
|
"@taiga-ui/cdk": "4.47.0",
|
||||||
"@taiga-ui/core": "4.44.0",
|
"@taiga-ui/core": "4.47.0",
|
||||||
"@taiga-ui/dompurify": "4.1.11",
|
"@taiga-ui/dompurify": "4.1.11",
|
||||||
"@taiga-ui/event-plugins": "4.6.0",
|
"@taiga-ui/event-plugins": "4.6.0",
|
||||||
"@taiga-ui/experimental": "4.44.0",
|
"@taiga-ui/experimental": "4.47.0",
|
||||||
"@taiga-ui/icons": "4.44.0",
|
"@taiga-ui/icons": "4.47.0",
|
||||||
"@taiga-ui/kit": "4.44.0",
|
"@taiga-ui/kit": "4.47.0",
|
||||||
"@taiga-ui/layout": "4.44.0",
|
"@taiga-ui/layout": "4.47.0",
|
||||||
"@taiga-ui/legacy": "4.44.0",
|
"@taiga-ui/legacy": "4.47.0",
|
||||||
"@taiga-ui/polymorpheus": "4.9.0",
|
"@taiga-ui/polymorpheus": "4.9.0",
|
||||||
"ansi-to-html": "^0.7.2",
|
"ansi-to-html": "^0.7.2",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
@@ -2978,9 +2978,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@maskito/angular": {
|
"node_modules/@maskito/angular": {
|
||||||
"version": "3.10.0",
|
"version": "3.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/@maskito/angular/-/angular-3.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@maskito/angular/-/angular-3.10.2.tgz",
|
||||||
"integrity": "sha512-5WwzV12MLJoCUD4ROEafUmyrElzGesWI4BqAFkh9jzzQRtrF1QNomK9tOVBXmXUBWb5sohiiNViAvRCtGdyXiA==",
|
"integrity": "sha512-+CQ7KQGmu35THj/59Uex+GotMFzdLHFUlPj5X5qphl+tHX09atmRzx7SEUCSEErbftTLafAFeR5N5t1fVTJvmw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2989,35 +2989,35 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@angular/forms": ">=16.0.0",
|
"@angular/forms": ">=16.0.0",
|
||||||
"@maskito/core": "^3.10.0"
|
"@maskito/core": "^3.10.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@maskito/core": {
|
"node_modules/@maskito/core": {
|
||||||
"version": "3.10.0",
|
"version": "3.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/@maskito/core/-/core-3.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@maskito/core/-/core-3.10.2.tgz",
|
||||||
"integrity": "sha512-T3PaMb4ipMmN9hkaAj8uyN0Mqj8XcXMZ1GRZ2WfZePRPHoi/L3tEEEh7vjg1m4TpI3lReRkNQs9yaPZV9ce8HA==",
|
"integrity": "sha512-LKh/PrG5wtMQ4AFYrWkKVGJUQB2CJcIt59qMPhntYIBpjw/OHWboHD4WWWQ94GvkYKjKQyjMcS/zvx+JaDrx2A==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@maskito/kit": {
|
"node_modules/@maskito/kit": {
|
||||||
"version": "3.10.0",
|
"version": "3.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-3.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-3.10.2.tgz",
|
||||||
"integrity": "sha512-b/aN200U0w/tNfLPRiXJaHGZRNVimq7UnhjKYoLXejX1+pKKhQ6S/dVg9k0+30IXdmUJ5Uk29y5X3UBc5d1w8A==",
|
"integrity": "sha512-d0YHheVt+DYZDL+A4uwoF0pF/rofczHz0KKYEuQrSdbKlRxOdyckQrj9iMCsmD73Hwne7LbjLL/rViHL4aFL2Q==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@maskito/core": "^3.10.0"
|
"@maskito/core": "^3.10.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@maskito/phone": {
|
"node_modules/@maskito/phone": {
|
||||||
"version": "3.10.0",
|
"version": "3.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/@maskito/phone/-/phone-3.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@maskito/phone/-/phone-3.10.2.tgz",
|
||||||
"integrity": "sha512-FrjC0l/SyLvSH7w+MG9v3lVT3OnD098dVCBR8HZlL6l5oI1Y69LTEBgRrNWIso841V8AEmj1ryJwrjHWX/zF5Q==",
|
"integrity": "sha512-XP/mp7CTHYriy6U+zoIitlJCGCmMr+yxtJ/u5y9+S4H3T1siILU3K8CAqetxpK//8/Zopco8lyz1D7ASKofdRg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@maskito/core": "^3.10.0",
|
"@maskito/core": "^3.10.2",
|
||||||
"@maskito/kit": "^3.10.0",
|
"@maskito/kit": "^3.10.2",
|
||||||
"libphonenumber-js": ">=1.0.0"
|
"libphonenumber-js": ">=1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -4713,9 +4713,9 @@
|
|||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/addon-charts": {
|
"node_modules/@taiga-ui/addon-charts": {
|
||||||
"version": "4.44.0",
|
"version": "4.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-4.47.0.tgz",
|
||||||
"integrity": "sha512-NiwY1P1NkDEOiSWgo3EGmXBWFmltmKA+Xkbu7fHnH7+8oenYLWd/orNvQEU5ey6qiSGj6wVr7kyeQMP8aau3NQ==",
|
"integrity": "sha512-BLMw9zNBJp2tC9PyuG0+7j5VrAL4QFrngGvVlSALjWy9Caj/4mHaoDp9PUwAQrsuoFIMc6BwTbDdbO6/DJeVUQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -4724,15 +4724,15 @@
|
|||||||
"@angular/common": ">=16.0.0",
|
"@angular/common": ">=16.0.0",
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@ng-web-apis/common": "^4.12.0",
|
"@ng-web-apis/common": "^4.12.0",
|
||||||
"@taiga-ui/cdk": "^4.44.0",
|
"@taiga-ui/cdk": "^4.47.0",
|
||||||
"@taiga-ui/core": "^4.44.0",
|
"@taiga-ui/core": "^4.47.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0"
|
"@taiga-ui/polymorpheus": "^4.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/addon-commerce": {
|
"node_modules/@taiga-ui/addon-commerce": {
|
||||||
"version": "4.44.0",
|
"version": "4.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-4.47.0.tgz",
|
||||||
"integrity": "sha512-hpYDis6cFDewm1PR6CTXDoOvGmPbqJwcrl1wOyAfmGWCY4rle9+Jj1P1fW4tDMkOY/TVhQ3GIuSUYn/UZN9v4A==",
|
"integrity": "sha512-Vh9kbQ47mUT6et3gc2/yJ7N6vebXDPRLHWBhpXxrXNVwel/dQT84NkvNBRJEPeSi2KRQNAy/qlxDdBVWUOFInw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -4741,22 +4741,22 @@
|
|||||||
"@angular/common": ">=16.0.0",
|
"@angular/common": ">=16.0.0",
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@angular/forms": ">=16.0.0",
|
"@angular/forms": ">=16.0.0",
|
||||||
"@maskito/angular": "^3.10.0",
|
"@maskito/angular": "^3.10.2",
|
||||||
"@maskito/core": "^3.10.0",
|
"@maskito/core": "^3.10.2",
|
||||||
"@maskito/kit": "^3.10.0",
|
"@maskito/kit": "^3.10.2",
|
||||||
"@ng-web-apis/common": "^4.12.0",
|
"@ng-web-apis/common": "^4.12.0",
|
||||||
"@taiga-ui/cdk": "^4.44.0",
|
"@taiga-ui/cdk": "^4.47.0",
|
||||||
"@taiga-ui/core": "^4.44.0",
|
"@taiga-ui/core": "^4.47.0",
|
||||||
"@taiga-ui/i18n": "^4.44.0",
|
"@taiga-ui/i18n": "^4.47.0",
|
||||||
"@taiga-ui/kit": "^4.44.0",
|
"@taiga-ui/kit": "^4.47.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/addon-mobile": {
|
"node_modules/@taiga-ui/addon-mobile": {
|
||||||
"version": "4.44.0",
|
"version": "4.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-mobile/-/addon-mobile-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-mobile/-/addon-mobile-4.47.0.tgz",
|
||||||
"integrity": "sha512-NTV6DpyrI6Pv9FuXiYwOsOljyKkrKX+HJ35SRev1hrxzw9ECKrnOA1Q0aUCn09PgWIUY7rV+OXUZJ/w/Hmlm5Q==",
|
"integrity": "sha512-rJeJUpXgEJyNNriiqLmVB7w8H+dSiECXjj1LRvSPffuL5bmvtBJKq8nw5Lpy+M3xeZz5qIMndJPNxSsKmkB0JA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -4766,18 +4766,18 @@
|
|||||||
"@angular/common": ">=16.0.0",
|
"@angular/common": ">=16.0.0",
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@ng-web-apis/common": "^4.12.0",
|
"@ng-web-apis/common": "^4.12.0",
|
||||||
"@taiga-ui/cdk": "^4.44.0",
|
"@taiga-ui/cdk": "^4.47.0",
|
||||||
"@taiga-ui/core": "^4.44.0",
|
"@taiga-ui/core": "^4.47.0",
|
||||||
"@taiga-ui/kit": "^4.44.0",
|
"@taiga-ui/kit": "^4.47.0",
|
||||||
"@taiga-ui/layout": "^4.44.0",
|
"@taiga-ui/layout": "^4.47.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/addon-table": {
|
"node_modules/@taiga-ui/addon-table": {
|
||||||
"version": "4.44.0",
|
"version": "4.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-table/-/addon-table-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-table/-/addon-table-4.47.0.tgz",
|
||||||
"integrity": "sha512-DmWZCPouoF21gWOpABepncWdg8W4Enk2z8FTiwGMsN0nGXmQ1SnU2QwEHCLumW2XNiNP8EHUpU42MZf9CuwIeA==",
|
"integrity": "sha512-5ZarUauEfPhr+S+nJIXZjyifcvim6Yi00cADI+0PmgitolArjHEB4ZrdosS+Iqfqj9znKF6gPkcmDlv6i0eMCg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -4786,18 +4786,18 @@
|
|||||||
"@angular/common": ">=16.0.0",
|
"@angular/common": ">=16.0.0",
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@ng-web-apis/intersection-observer": "^4.12.0",
|
"@ng-web-apis/intersection-observer": "^4.12.0",
|
||||||
"@taiga-ui/cdk": "^4.44.0",
|
"@taiga-ui/cdk": "^4.47.0",
|
||||||
"@taiga-ui/core": "^4.44.0",
|
"@taiga-ui/core": "^4.47.0",
|
||||||
"@taiga-ui/i18n": "^4.44.0",
|
"@taiga-ui/i18n": "^4.47.0",
|
||||||
"@taiga-ui/kit": "^4.44.0",
|
"@taiga-ui/kit": "^4.47.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/cdk": {
|
"node_modules/@taiga-ui/cdk": {
|
||||||
"version": "4.44.0",
|
"version": "4.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-4.47.0.tgz",
|
||||||
"integrity": "sha512-4x0ISp+0oYhTC2E9SN2yDhA+rFC707m/rL4lf7RSTIbdefMyP3TdmsN2Emuhc0WSXcNs0OgYYEOaIx9jr7OqDw==",
|
"integrity": "sha512-TqUg+7p/IZqlk34IB4/ZqTfw7HXifX2SqL9psCEmtW5Pg9zWN9of0S9g6Ccj9ALF+4Q4JxuHQf/xQdtAXdYdqg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "2.8.1"
|
"tslib": "2.8.1"
|
||||||
@@ -4807,7 +4807,7 @@
|
|||||||
"@angular-devkit/schematics": ">=16.0.0",
|
"@angular-devkit/schematics": ">=16.0.0",
|
||||||
"@schematics/angular": ">=16.0.0",
|
"@schematics/angular": ">=16.0.0",
|
||||||
"ng-morph": "^4.8.4",
|
"ng-morph": "^4.8.4",
|
||||||
"parse5": ">=7.3.0"
|
"parse5": "^7.3.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/animations": ">=16.0.0",
|
"@angular/animations": ">=16.0.0",
|
||||||
@@ -4826,9 +4826,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/core": {
|
"node_modules/@taiga-ui/core": {
|
||||||
"version": "4.44.0",
|
"version": "4.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-4.47.0.tgz",
|
||||||
"integrity": "sha512-KQPD63ZoFJKBZS/m1XkyTXDzUH3IYDcgNE3CVeZ9bmEkRwS0x7/fpFFdbr06sR/ej/eEzgop3LKOk4JHoQgFCA==",
|
"integrity": "sha512-Z/6djJcMWn4/gFcdW6BDd7GU5tkGnidmfVFhSMCFoRYIY2YU7USjp6wYkjC9jhpRYZZVWVwY3IX4/GJGG5gLQg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -4842,9 +4842,9 @@
|
|||||||
"@angular/router": ">=16.0.0",
|
"@angular/router": ">=16.0.0",
|
||||||
"@ng-web-apis/common": "^4.12.0",
|
"@ng-web-apis/common": "^4.12.0",
|
||||||
"@ng-web-apis/mutation-observer": "^4.12.0",
|
"@ng-web-apis/mutation-observer": "^4.12.0",
|
||||||
"@taiga-ui/cdk": "^4.44.0",
|
"@taiga-ui/cdk": "^4.47.0",
|
||||||
"@taiga-ui/event-plugins": "^4.6.0",
|
"@taiga-ui/event-plugins": "^4.6.0",
|
||||||
"@taiga-ui/i18n": "^4.44.0",
|
"@taiga-ui/i18n": "^4.47.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
@@ -4879,9 +4879,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/experimental": {
|
"node_modules/@taiga-ui/experimental": {
|
||||||
"version": "4.44.0",
|
"version": "4.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-4.47.0.tgz",
|
||||||
"integrity": "sha512-ef/fSFjSOTwgsqRoWlwlXQkQrPcbuAlmEyGj4gMhdZC99HvKuYzUdihH4eZX4nx8fDyBOOZbM7QnW7ETQbKWHg==",
|
"integrity": "sha512-EkswWcDKwtDhjA5A5oMtYHw9fnRBITpVb3cOwG45lQrlFBP0tipmx2QVlKKu0ga8VPjcv3W+b/pZsOWdp7UkJQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -4889,18 +4889,18 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/common": ">=16.0.0",
|
"@angular/common": ">=16.0.0",
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@taiga-ui/addon-commerce": "^4.44.0",
|
"@taiga-ui/addon-commerce": "^4.47.0",
|
||||||
"@taiga-ui/cdk": "^4.44.0",
|
"@taiga-ui/cdk": "^4.47.0",
|
||||||
"@taiga-ui/core": "^4.44.0",
|
"@taiga-ui/core": "^4.47.0",
|
||||||
"@taiga-ui/kit": "^4.44.0",
|
"@taiga-ui/kit": "^4.47.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/i18n": {
|
"node_modules/@taiga-ui/i18n": {
|
||||||
"version": "4.44.0",
|
"version": "4.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.47.0.tgz",
|
||||||
"integrity": "sha512-SnRQQPKI1k3Tkn17yByuB1ix4Nj9v70RLxVM84GX3okgQk2NIAj9hn8WXd36ljZUlcy7TqtJ+xlii0FqQVv7Sw==",
|
"integrity": "sha512-TC9BugM8W7IgIXy3IoLtEWlxIb0xAxm17bfAtVLH9M8BfuQY6Jk0yHVQAspmaEz8pmaoLx9Tl2bXV9ugYG/ypA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4913,18 +4913,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/icons": {
|
"node_modules/@taiga-ui/icons": {
|
||||||
"version": "4.44.0",
|
"version": "4.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-4.47.0.tgz",
|
||||||
"integrity": "sha512-KiIxxkG59kJDFV/g6e1YAbBff7y7EynNbmKY6sGo+CLcAQ52Y7Cz6i9us6ySf8fbOQRm4hKDXL3M10TKDeS+cg==",
|
"integrity": "sha512-/SV6RdsCZoX5uUIlHiAPMLNNY7j8nxmE8NhGFtj4E5szx+V84LIKh2oES+zawPa7lIcVUT5M+FtXAtqdMiQa2g==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/kit": {
|
"node_modules/@taiga-ui/kit": {
|
||||||
"version": "4.44.0",
|
"version": "4.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-4.47.0.tgz",
|
||||||
"integrity": "sha512-X8JNEE/WR7ftEx/bmibe2JTPOwge/o1VYHk1Z2vDx53e8JWWG+lLce2xx7NYexFPqP+4Ey2bPxn1Vx+rgGZYgA==",
|
"integrity": "sha512-7LZNA4QInvB76Q38DRD1Ba2vIiKrpk4b/IoSmpahojYU1nrYByY69jGGD7ImxmCMJaIxymaODwHTcEJd3hW4Sw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -4934,25 +4934,25 @@
|
|||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@angular/forms": ">=16.0.0",
|
"@angular/forms": ">=16.0.0",
|
||||||
"@angular/router": ">=16.0.0",
|
"@angular/router": ">=16.0.0",
|
||||||
"@maskito/angular": "^3.10.0",
|
"@maskito/angular": "^3.10.2",
|
||||||
"@maskito/core": "^3.10.0",
|
"@maskito/core": "^3.10.2",
|
||||||
"@maskito/kit": "^3.10.0",
|
"@maskito/kit": "^3.10.2",
|
||||||
"@maskito/phone": "^3.10.0",
|
"@maskito/phone": "^3.10.2",
|
||||||
"@ng-web-apis/common": "^4.12.0",
|
"@ng-web-apis/common": "^4.12.0",
|
||||||
"@ng-web-apis/intersection-observer": "^4.12.0",
|
"@ng-web-apis/intersection-observer": "^4.12.0",
|
||||||
"@ng-web-apis/mutation-observer": "^4.12.0",
|
"@ng-web-apis/mutation-observer": "^4.12.0",
|
||||||
"@ng-web-apis/resize-observer": "^4.12.0",
|
"@ng-web-apis/resize-observer": "^4.12.0",
|
||||||
"@taiga-ui/cdk": "^4.44.0",
|
"@taiga-ui/cdk": "^4.47.0",
|
||||||
"@taiga-ui/core": "^4.44.0",
|
"@taiga-ui/core": "^4.47.0",
|
||||||
"@taiga-ui/i18n": "^4.44.0",
|
"@taiga-ui/i18n": "^4.47.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/layout": {
|
"node_modules/@taiga-ui/layout": {
|
||||||
"version": "4.44.0",
|
"version": "4.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/layout/-/layout-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/layout/-/layout-4.47.0.tgz",
|
||||||
"integrity": "sha512-aidH7MgEAcz0nxauc5tm+0a5Z7PmG9bsSq+tsfkiOd6mV4BOf5xyYlpDyQn9gUBK8UYSxtMojp9Zs/2jH57gmw==",
|
"integrity": "sha512-/P1dhno9/gvUT8lkbJGbNE8etm2D4G+2nRJAQwgj2Az2VePdR39nTOq0NKlGPyyjvSGi21/Mq2Hr5rRig3aglw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -4960,17 +4960,17 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/common": ">=16.0.0",
|
"@angular/common": ">=16.0.0",
|
||||||
"@angular/core": ">=16.0.0",
|
"@angular/core": ">=16.0.0",
|
||||||
"@taiga-ui/cdk": "^4.44.0",
|
"@taiga-ui/cdk": "^4.47.0",
|
||||||
"@taiga-ui/core": "^4.44.0",
|
"@taiga-ui/core": "^4.47.0",
|
||||||
"@taiga-ui/kit": "^4.44.0",
|
"@taiga-ui/kit": "^4.47.0",
|
||||||
"@taiga-ui/polymorpheus": "^4.9.0",
|
"@taiga-ui/polymorpheus": "^4.9.0",
|
||||||
"rxjs": ">=7.0.0"
|
"rxjs": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@taiga-ui/legacy": {
|
"node_modules/@taiga-ui/legacy": {
|
||||||
"version": "4.44.0",
|
"version": "4.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/@taiga-ui/legacy/-/legacy-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@taiga-ui/legacy/-/legacy-4.47.0.tgz",
|
||||||
"integrity": "sha512-vVXEj2BkKAS3ShOQ8OV6+j2yGFcRqtXMxZEU3kX8iM4fWRYS3C1669LLK7f0VzZd1miNex79SZx3NJM4Rvx7Kg==",
|
"integrity": "sha512-+82AOSKr2D/d8WSwdHZxW1BdL1fAh7HQEyhjCNT00hCaeQAeQ5QZNzDRX3qxFKwWZPWkC/sG8a/AtDtjkjB7Ew==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": ">=2.8.1"
|
"tslib": ">=2.8.1"
|
||||||
@@ -8456,9 +8456,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/libphonenumber-js": {
|
"node_modules/libphonenumber-js": {
|
||||||
"version": "1.12.9",
|
"version": "1.12.10",
|
||||||
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.9.tgz",
|
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.10.tgz",
|
||||||
"integrity": "sha512-VWwAdNeJgN7jFOD+wN4qx83DTPMVPPAUyx9/TUkBXKLiNkuWWk6anV0439tgdtwaJDrEdqkvdN22iA6J4bUCZg==",
|
"integrity": "sha512-E91vHJD61jekHHR/RF/E83T/CMoaLXT7cwYA75T4gim4FZjnM6hbJjVIGg7chqlSqRsSvQ3izGmOjHy1SQzcGQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -46,18 +46,18 @@
|
|||||||
"@noble/hashes": "^1.4.0",
|
"@noble/hashes": "^1.4.0",
|
||||||
"@start9labs/argon2": "^0.3.0",
|
"@start9labs/argon2": "^0.3.0",
|
||||||
"@start9labs/start-sdk": "file:../sdk/baseDist",
|
"@start9labs/start-sdk": "file:../sdk/baseDist",
|
||||||
"@taiga-ui/addon-charts": "4.44.0",
|
"@taiga-ui/addon-charts": "4.47.0",
|
||||||
"@taiga-ui/addon-commerce": "4.44.0",
|
"@taiga-ui/addon-commerce": "4.47.0",
|
||||||
"@taiga-ui/addon-mobile": "4.44.0",
|
"@taiga-ui/addon-mobile": "4.47.0",
|
||||||
"@taiga-ui/addon-table": "4.44.0",
|
"@taiga-ui/addon-table": "4.47.0",
|
||||||
"@taiga-ui/cdk": "4.44.0",
|
"@taiga-ui/cdk": "4.47.0",
|
||||||
"@taiga-ui/core": "4.44.0",
|
"@taiga-ui/core": "4.47.0",
|
||||||
"@taiga-ui/event-plugins": "4.6.0",
|
"@taiga-ui/event-plugins": "4.6.0",
|
||||||
"@taiga-ui/experimental": "4.44.0",
|
"@taiga-ui/experimental": "4.47.0",
|
||||||
"@taiga-ui/icons": "4.44.0",
|
"@taiga-ui/icons": "4.47.0",
|
||||||
"@taiga-ui/kit": "4.44.0",
|
"@taiga-ui/kit": "4.47.0",
|
||||||
"@taiga-ui/layout": "4.44.0",
|
"@taiga-ui/layout": "4.47.0",
|
||||||
"@taiga-ui/legacy": "4.44.0",
|
"@taiga-ui/legacy": "4.47.0",
|
||||||
"@taiga-ui/polymorpheus": "4.9.0",
|
"@taiga-ui/polymorpheus": "4.9.0",
|
||||||
"@taiga-ui/dompurify": "4.1.11",
|
"@taiga-ui/dompurify": "4.1.11",
|
||||||
"ansi-to-html": "^0.7.2",
|
"ansi-to-html": "^0.7.2",
|
||||||
|
|||||||
@@ -539,4 +539,7 @@ export default {
|
|||||||
543: 'Gateway ändern',
|
543: 'Gateway ändern',
|
||||||
544: 'Standard-ACME ändern',
|
544: 'Standard-ACME ändern',
|
||||||
545: 'Keine Domains',
|
545: 'Keine Domains',
|
||||||
|
546: 'Anbieter',
|
||||||
|
547: 'DNS anzeigen',
|
||||||
|
548: 'DNS testen',
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -538,4 +538,7 @@ export const ENGLISH = {
|
|||||||
'Change gateway': 543, // as in, change the network gateway for a computer
|
'Change gateway': 543, // as in, change the network gateway for a computer
|
||||||
'Change default ACME': 544, // as in, change the default ACME provider for a domain
|
'Change default ACME': 544, // as in, change the default ACME provider for a domain
|
||||||
'No domains': 545,
|
'No domains': 545,
|
||||||
|
'Provider': 546,
|
||||||
|
'Show DNS': 547,
|
||||||
|
'Test DNS': 548,
|
||||||
} as const
|
} as const
|
||||||
|
|||||||
@@ -539,4 +539,7 @@ export default {
|
|||||||
543: 'Cambiar puerta de enlace',
|
543: 'Cambiar puerta de enlace',
|
||||||
544: 'Cambiar ACME predeterminado',
|
544: 'Cambiar ACME predeterminado',
|
||||||
545: 'Sin dominios',
|
545: 'Sin dominios',
|
||||||
|
546: 'Proveedor',
|
||||||
|
547: 'Mostrar DNS',
|
||||||
|
548: 'Probar DNS',
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -539,4 +539,7 @@ export default {
|
|||||||
543: 'Changer de passerelle',
|
543: 'Changer de passerelle',
|
||||||
544: 'Changer l’ACME par défaut',
|
544: 'Changer l’ACME par défaut',
|
||||||
545: 'Aucun domaine',
|
545: 'Aucun domaine',
|
||||||
|
546: 'Fournisseur',
|
||||||
|
547: 'Afficher le DNS',
|
||||||
|
548: 'Tester le DNS',
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -539,4 +539,7 @@ export default {
|
|||||||
543: 'Zmień bramę',
|
543: 'Zmień bramę',
|
||||||
544: 'Zmień domyślny ACME',
|
544: 'Zmień domyślny ACME',
|
||||||
545: 'Brak domen',
|
545: 'Brak domen',
|
||||||
|
546: 'Dostawca',
|
||||||
|
547: 'Pokaż DNS',
|
||||||
|
548: 'Test DNS',
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -49,13 +49,13 @@ export class DialogService {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
options.data = options.data || {}
|
const { content, yes, no } = options.data || {}
|
||||||
const { content, yes, no } = options.data
|
|
||||||
|
|
||||||
return this.dialogs.open<T>(TUI_CONFIRM, {
|
return this.dialogs.open<T>(TUI_CONFIRM, {
|
||||||
|
...options,
|
||||||
label: this.i18n.transform(options.label),
|
label: this.i18n.transform(options.label),
|
||||||
data: {
|
data: {
|
||||||
...options.data,
|
...(options.data || {}),
|
||||||
content: isI18n(content) ? this.i18n.transform(content) : content,
|
content: isI18n(content) ? this.i18n.transform(content) : content,
|
||||||
yes: this.i18n.transform(yes),
|
yes: this.i18n.transform(yes),
|
||||||
no: this.i18n.transform(no),
|
no: this.i18n.transform(no),
|
||||||
|
|||||||
@@ -24,5 +24,5 @@ import { i18nKey, i18nPipe } from '@start9labs/shared'
|
|||||||
imports: [i18nPipe],
|
imports: [i18nPipe],
|
||||||
})
|
})
|
||||||
export class TableComponent {
|
export class TableComponent {
|
||||||
readonly appTable = input.required<Array<i18nKey | null>>()
|
readonly appTable = input.required<ReadonlyArray<i18nKey | null>>()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
inject,
|
||||||
|
input,
|
||||||
|
} from '@angular/core'
|
||||||
|
import { i18nPipe } from '@start9labs/shared'
|
||||||
|
import {
|
||||||
|
TuiButton,
|
||||||
|
TuiDataList,
|
||||||
|
TuiDropdown,
|
||||||
|
TuiTextfield,
|
||||||
|
} from '@taiga-ui/core'
|
||||||
|
import { toAcmeName } from 'src/app/utils/acme'
|
||||||
|
|
||||||
|
import { AcmeService } from './acme.service'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tr[acme]',
|
||||||
|
template: `
|
||||||
|
<td>{{ toAcmeName(acme().url) }}</td>
|
||||||
|
<td>{{ acme().contact.join(', ') }}</td>
|
||||||
|
<td>
|
||||||
|
<button
|
||||||
|
tuiIconButton
|
||||||
|
tuiDropdown
|
||||||
|
size="s"
|
||||||
|
appearance="flat-grayscale"
|
||||||
|
iconStart="@tui.ellipsis-vertical"
|
||||||
|
[tuiAppearanceState]="open ? 'hover' : null"
|
||||||
|
[(tuiDropdownOpen)]="open"
|
||||||
|
>
|
||||||
|
{{ 'More' | i18n }}
|
||||||
|
<tui-data-list size="s" *tuiTextfieldDropdown>
|
||||||
|
<tui-opt-group>
|
||||||
|
<button
|
||||||
|
tuiOption
|
||||||
|
new
|
||||||
|
iconStart="@tui.pencil"
|
||||||
|
(click)="service.edit(acme())"
|
||||||
|
>
|
||||||
|
{{ 'Edit' | i18n }}
|
||||||
|
</button>
|
||||||
|
</tui-opt-group>
|
||||||
|
<tui-opt-group>
|
||||||
|
<button
|
||||||
|
tuiOption
|
||||||
|
new
|
||||||
|
iconStart="@tui.trash"
|
||||||
|
class="g-negative"
|
||||||
|
(click)="service.remove(acme())"
|
||||||
|
>
|
||||||
|
{{ 'Delete' | i18n }}
|
||||||
|
</button>
|
||||||
|
</tui-opt-group>
|
||||||
|
</tui-data-list>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
`,
|
||||||
|
styles: `
|
||||||
|
td:last-child {
|
||||||
|
grid-area: 1 / 2 / 3;
|
||||||
|
align-self: center;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host-context(tui-root._mobile) {
|
||||||
|
grid-template-columns: 1fr min-content;
|
||||||
|
|
||||||
|
td:first-child {
|
||||||
|
font: var(--tui-font-text-m);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [TuiButton, i18nPipe, TuiDropdown, TuiDataList, TuiTextfield],
|
||||||
|
})
|
||||||
|
export class DomainsAcmeComponent {
|
||||||
|
protected readonly service = inject(AcmeService)
|
||||||
|
|
||||||
|
readonly acme = input.required<{ url: string; contact: readonly string[] }>()
|
||||||
|
|
||||||
|
open = false
|
||||||
|
|
||||||
|
toAcmeName = toAcmeName
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
import { inject, Injectable } from '@angular/core'
|
||||||
|
import {
|
||||||
|
DialogService,
|
||||||
|
ErrorService,
|
||||||
|
i18nPipe,
|
||||||
|
LoadingService,
|
||||||
|
} from '@start9labs/shared'
|
||||||
|
import { ISB, utils } from '@start9labs/start-sdk'
|
||||||
|
import { filter } from 'rxjs'
|
||||||
|
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
||||||
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
|
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||||
|
import { knownACME } from 'src/app/utils/acme'
|
||||||
|
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class AcmeService {
|
||||||
|
private readonly loader = inject(LoadingService)
|
||||||
|
private readonly errorService = inject(ErrorService)
|
||||||
|
private readonly api = inject(ApiService)
|
||||||
|
private readonly formDialog = inject(FormDialogService)
|
||||||
|
private readonly i18n = inject(i18nPipe)
|
||||||
|
private readonly dialog = inject(DialogService)
|
||||||
|
|
||||||
|
async add(providers: { url: string; contact: string[] }[]) {
|
||||||
|
this.formDialog.open(FormComponent, {
|
||||||
|
label: 'Add ACME Provider',
|
||||||
|
data: {
|
||||||
|
spec: await configBuilderToSpec(
|
||||||
|
this.addSpec(providers.map(p => p.url)),
|
||||||
|
),
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: this.i18n.transform('Save'),
|
||||||
|
handler: async (val: ReturnType<typeof this.addSpec>['_TYPE']) => {
|
||||||
|
const providerUrl =
|
||||||
|
val.provider.selection === 'other'
|
||||||
|
? val.provider.value.url
|
||||||
|
: val.provider.selection
|
||||||
|
|
||||||
|
return this.save(providerUrl, val.contact)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async edit({ url, contact }: { url: string; contact: readonly string[] }) {
|
||||||
|
this.formDialog.open(FormComponent, {
|
||||||
|
label: 'Edit ACME Provider',
|
||||||
|
data: {
|
||||||
|
spec: await configBuilderToSpec(this.editSpec()),
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: this.i18n.transform('Save'),
|
||||||
|
handler: async (val: ReturnType<typeof this.editSpec>['_TYPE']) =>
|
||||||
|
this.save(url, val.contact),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value: { contact },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
remove({ url }: { url: string }) {
|
||||||
|
this.dialog
|
||||||
|
.openConfirm({ label: 'Are you sure?', size: 's' })
|
||||||
|
.pipe(filter(Boolean))
|
||||||
|
.subscribe(async () => {
|
||||||
|
const loader = this.loader.open('Removing').subscribe()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.api.removeAcme({ provider: url })
|
||||||
|
} catch (e: any) {
|
||||||
|
this.errorService.handleError(e)
|
||||||
|
} finally {
|
||||||
|
loader.unsubscribe()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private async save(providerUrl: string, contact: readonly string[]) {
|
||||||
|
const loader = this.loader.open('Saving').subscribe()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.api.initAcme({
|
||||||
|
provider: new URL(providerUrl).href,
|
||||||
|
contact: contact.map(address => `mailto:${address}`),
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
} catch (e: any) {
|
||||||
|
this.errorService.handleError(e)
|
||||||
|
return false
|
||||||
|
} finally {
|
||||||
|
loader.unsubscribe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private addSpec(providers: string[]) {
|
||||||
|
const availableAcme = knownACME.filter(
|
||||||
|
acme => !providers.includes(acme.url),
|
||||||
|
)
|
||||||
|
|
||||||
|
return ISB.InputSpec.of({
|
||||||
|
provider: ISB.Value.union({
|
||||||
|
name: 'Provider',
|
||||||
|
default: (availableAcme[0]?.url as any) || 'other',
|
||||||
|
variants: ISB.Variants.of({
|
||||||
|
...availableAcme.reduce(
|
||||||
|
(obj, curr) => ({
|
||||||
|
...obj,
|
||||||
|
[curr.url]: {
|
||||||
|
name: curr.name,
|
||||||
|
spec: ISB.InputSpec.of({}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
other: {
|
||||||
|
name: 'Other',
|
||||||
|
spec: ISB.InputSpec.of({
|
||||||
|
url: ISB.Value.text({
|
||||||
|
name: 'URL',
|
||||||
|
default: null,
|
||||||
|
required: true,
|
||||||
|
inputmode: 'url',
|
||||||
|
patterns: [utils.Patterns.url],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
contact: this.emailListSpec(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private editSpec() {
|
||||||
|
return ISB.InputSpec.of({
|
||||||
|
contact: this.emailListSpec(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private emailListSpec() {
|
||||||
|
return ISB.Value.list(
|
||||||
|
ISB.List.text(
|
||||||
|
{
|
||||||
|
name: this.i18n.transform('Contact Emails')!,
|
||||||
|
description: this.i18n.transform(
|
||||||
|
'Needed to obtain a certificate from a Certificate Authority',
|
||||||
|
),
|
||||||
|
minLength: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputmode: 'email',
|
||||||
|
patterns: [utils.Patterns.email],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
inject,
|
||||||
|
input,
|
||||||
|
} from '@angular/core'
|
||||||
|
import {
|
||||||
|
DialogService,
|
||||||
|
ErrorService,
|
||||||
|
i18nPipe,
|
||||||
|
LoadingService,
|
||||||
|
} from '@start9labs/shared'
|
||||||
|
import { ISB } from '@start9labs/start-sdk'
|
||||||
|
import {
|
||||||
|
TuiButton,
|
||||||
|
TuiDataList,
|
||||||
|
TuiDropdown,
|
||||||
|
TuiTextfield,
|
||||||
|
} from '@taiga-ui/core'
|
||||||
|
import { filter } from 'rxjs'
|
||||||
|
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
||||||
|
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||||
|
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tr[domain]',
|
||||||
|
template: `
|
||||||
|
<td>{{ domain().domain }}</td>
|
||||||
|
<td [style.order]="-1">{{ domain().gateway }}</td>
|
||||||
|
<td>{{ domain().acme }}</td>
|
||||||
|
<td>
|
||||||
|
<button
|
||||||
|
tuiIconButton
|
||||||
|
tuiDropdown
|
||||||
|
size="s"
|
||||||
|
appearance="flat-grayscale"
|
||||||
|
iconStart="@tui.ellipsis-vertical"
|
||||||
|
[tuiAppearanceState]="open ? 'hover' : null"
|
||||||
|
[(tuiDropdownOpen)]="open"
|
||||||
|
>
|
||||||
|
{{ 'More' | i18n }}
|
||||||
|
<tui-data-list size="s" *tuiTextfieldDropdown>
|
||||||
|
<tui-opt-group>
|
||||||
|
<button tuiOption new iconStart="@tui.pencil" (click)="edit()">
|
||||||
|
{{ 'Edit' | i18n }}
|
||||||
|
</button>
|
||||||
|
<button tuiOption new iconStart="@tui.shield" (click)="showDns()">
|
||||||
|
{{ 'Show DNS' | i18n }}
|
||||||
|
</button>
|
||||||
|
<button tuiOption new iconStart="@tui.shield" (click)="testDns()">
|
||||||
|
{{ 'Test DNS' | i18n }}
|
||||||
|
</button>
|
||||||
|
</tui-opt-group>
|
||||||
|
<tui-opt-group>
|
||||||
|
<button
|
||||||
|
tuiOption
|
||||||
|
new
|
||||||
|
iconStart="@tui.trash"
|
||||||
|
class="g-negative"
|
||||||
|
(click)="remove()"
|
||||||
|
>
|
||||||
|
{{ 'Delete' | i18n }}
|
||||||
|
</button>
|
||||||
|
</tui-opt-group>
|
||||||
|
</tui-data-list>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
`,
|
||||||
|
styles: `
|
||||||
|
td:last-child {
|
||||||
|
grid-area: 1 / 2 / 4;
|
||||||
|
align-self: center;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host-context(tui-root._mobile) {
|
||||||
|
grid-template-columns: 1fr min-content;
|
||||||
|
|
||||||
|
td:first-child {
|
||||||
|
font: var(--tui-font-text-m);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [TuiButton, i18nPipe, TuiDropdown, TuiDataList, TuiTextfield],
|
||||||
|
})
|
||||||
|
export class DomainsDomainComponent {
|
||||||
|
private readonly dialog = inject(DialogService)
|
||||||
|
private readonly loader = inject(LoadingService)
|
||||||
|
private readonly errorService = inject(ErrorService)
|
||||||
|
private readonly formDialog = inject(FormDialogService)
|
||||||
|
|
||||||
|
readonly domain = input.required<any>()
|
||||||
|
|
||||||
|
open = false
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
this.dialog
|
||||||
|
.openConfirm({ label: 'Are you sure?', size: 's' })
|
||||||
|
.pipe(filter(Boolean))
|
||||||
|
.subscribe(async () => {
|
||||||
|
const loader = this.loader.open('Deleting').subscribe()
|
||||||
|
|
||||||
|
try {
|
||||||
|
} catch (e: any) {
|
||||||
|
this.errorService.handleError(e)
|
||||||
|
} finally {
|
||||||
|
loader.unsubscribe()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async edit() {
|
||||||
|
const renameSpec = ISB.InputSpec.of({})
|
||||||
|
|
||||||
|
this.formDialog.open(FormComponent, {
|
||||||
|
label: 'Edit',
|
||||||
|
data: {
|
||||||
|
spec: await configBuilderToSpec(renameSpec),
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Save',
|
||||||
|
handler: (value: typeof renameSpec._TYPE) => {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async showDns() {}
|
||||||
|
|
||||||
|
async testDns() {}
|
||||||
|
}
|
||||||
@@ -6,25 +6,15 @@ import {
|
|||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { toSignal } from '@angular/core/rxjs-interop'
|
import { toSignal } from '@angular/core/rxjs-interop'
|
||||||
import { RouterLink } from '@angular/router'
|
import { RouterLink } from '@angular/router'
|
||||||
import {
|
import { DocsLinkDirective, i18nPipe } from '@start9labs/shared'
|
||||||
DocsLinkDirective,
|
import { TuiButton, TuiLink, TuiTitle } from '@taiga-ui/core'
|
||||||
ErrorService,
|
import { TuiHeader } from '@taiga-ui/layout'
|
||||||
i18nPipe,
|
|
||||||
LoadingService,
|
|
||||||
} from '@start9labs/shared'
|
|
||||||
import { ISB, utils } from '@start9labs/start-sdk'
|
|
||||||
import { TuiButton, TuiLink, TuiLoader, TuiTitle } from '@taiga-ui/core'
|
|
||||||
import { TuiCell, TuiHeader } from '@taiga-ui/layout'
|
|
||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
import { map } from 'rxjs'
|
import { map } from 'rxjs'
|
||||||
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
|
||||||
import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
|
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
|
||||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
|
||||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||||
import { TitleDirective } from 'src/app/services/title.service'
|
import { TitleDirective } from 'src/app/services/title.service'
|
||||||
import { knownACME, toAcmeName } from 'src/app/utils/acme'
|
|
||||||
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
import { AcmeService } from './acme.service'
|
||||||
import { DomainsTableComponent } from './table.component'
|
import { DomainsTableComponent } from './table.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -65,46 +55,13 @@ import { DomainsTableComponent } from './table.component'
|
|||||||
size="xs"
|
size="xs"
|
||||||
iconStart="@tui.plus"
|
iconStart="@tui.plus"
|
||||||
[style.margin-inline-start]="'auto'"
|
[style.margin-inline-start]="'auto'"
|
||||||
(click)="addAcme(value)"
|
(click)="service.add(value)"
|
||||||
>
|
>
|
||||||
{{ 'Add' | i18n }}
|
{{ 'Add' | i18n }}
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
</header>
|
</header>
|
||||||
@if (acme(); as value) {
|
<domains-table mode="acme" [items]="acme()" />
|
||||||
@for (provider of value; track $index) {
|
|
||||||
<div tuiCell>
|
|
||||||
<span tuiTitle>
|
|
||||||
<strong>{{ toAcmeName(provider.url) }}</strong>
|
|
||||||
<span tuiSubtitle>
|
|
||||||
{{ 'Contact' | i18n }}: {{ provider.contactString }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
tuiIconButton
|
|
||||||
iconStart="@tui.pencil"
|
|
||||||
appearance="icon"
|
|
||||||
(click)="editAcme(provider.url, provider.contact)"
|
|
||||||
>
|
|
||||||
{{ 'Edit' | i18n }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
tuiIconButton
|
|
||||||
iconStart="@tui.trash"
|
|
||||||
appearance="icon"
|
|
||||||
(click)="removeAcme(provider.url)"
|
|
||||||
>
|
|
||||||
{{ 'Edit' | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
} @empty {
|
|
||||||
<app-placeholder icon="@tui.shield-question">
|
|
||||||
{{ 'No saved providers' | i18n }}
|
|
||||||
</app-placeholder>
|
|
||||||
}
|
|
||||||
} @else {
|
|
||||||
<tui-loader [style.height.rem]="5" />
|
|
||||||
}
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="g-card">
|
<section class="g-card">
|
||||||
@@ -113,22 +70,20 @@ import { DomainsTableComponent } from './table.component'
|
|||||||
<button
|
<button
|
||||||
tuiButton
|
tuiButton
|
||||||
size="xs"
|
size="xs"
|
||||||
[style.margin]="'0 0.5rem 0 auto'"
|
|
||||||
iconStart="@tui.plus"
|
iconStart="@tui.plus"
|
||||||
|
[style.margin-inline-start]="'auto'"
|
||||||
(click)="addDomain()"
|
(click)="addDomain()"
|
||||||
>
|
>
|
||||||
Add
|
Add
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
<div #table [domains]="domains()"></div>
|
<domains-table mode="domains" [items]="domains()" />
|
||||||
</section>
|
</section>
|
||||||
`,
|
`,
|
||||||
styles: ``,
|
styles: ``,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
imports: [
|
||||||
TuiButton,
|
TuiButton,
|
||||||
TuiLoader,
|
|
||||||
TuiCell,
|
|
||||||
TuiTitle,
|
TuiTitle,
|
||||||
TuiHeader,
|
TuiHeader,
|
||||||
TuiLink,
|
TuiLink,
|
||||||
@@ -136,179 +91,38 @@ import { DomainsTableComponent } from './table.component'
|
|||||||
TitleDirective,
|
TitleDirective,
|
||||||
i18nPipe,
|
i18nPipe,
|
||||||
DocsLinkDirective,
|
DocsLinkDirective,
|
||||||
PlaceholderComponent,
|
|
||||||
DomainsTableComponent,
|
DomainsTableComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export default class SystemDomainsComponent {
|
export default class SystemDomainsComponent {
|
||||||
private readonly formDialog = inject(FormDialogService)
|
protected readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||||
private readonly loader = inject(LoadingService)
|
protected readonly service = inject(AcmeService)
|
||||||
private readonly errorService = inject(ErrorService)
|
|
||||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
|
||||||
private readonly api = inject(ApiService)
|
|
||||||
private readonly i18n = inject(i18nPipe)
|
|
||||||
|
|
||||||
acme = toSignal(
|
readonly acme = toSignal(
|
||||||
this.patch.watch$('serverInfo', 'network', 'acme').pipe(
|
this.patch.watch$('serverInfo', 'network', 'acme').pipe(
|
||||||
map(acme =>
|
map(acme =>
|
||||||
Object.keys(acme).map(url => {
|
Object.keys(acme).map(url => ({
|
||||||
const contact =
|
url,
|
||||||
|
contact:
|
||||||
acme[url]?.contact.map(mailto => mailto.replace('mailto:', '')) ||
|
acme[url]?.contact.map(mailto => mailto.replace('mailto:', '')) ||
|
||||||
[]
|
[],
|
||||||
return {
|
})),
|
||||||
url,
|
|
||||||
contact,
|
|
||||||
contactString: contact.join(', '),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
domains = signal([])
|
readonly domains = signal([
|
||||||
|
{
|
||||||
toAcmeName = toAcmeName
|
domain: 'blog.mydomain.com',
|
||||||
|
gateway: 'StartTunnel',
|
||||||
async addAcme(
|
acme: 'System',
|
||||||
providers: {
|
},
|
||||||
url: string
|
{
|
||||||
contact: string[]
|
domain: 'blog. mydomain.com',
|
||||||
contactString: string
|
gateway: 'StartTunnel',
|
||||||
}[],
|
acme: 'System',
|
||||||
) {
|
},
|
||||||
this.formDialog.open(FormComponent, {
|
])
|
||||||
label: 'Add ACME Provider',
|
|
||||||
data: {
|
|
||||||
spec: await configBuilderToSpec(
|
|
||||||
this.addAcmeSpec(providers.map(p => p.url)),
|
|
||||||
),
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: this.i18n.transform('Save'),
|
|
||||||
handler: async (
|
|
||||||
val: ReturnType<typeof this.addAcmeSpec>['_TYPE'],
|
|
||||||
) => {
|
|
||||||
const providerUrl =
|
|
||||||
val.provider.selection === 'other'
|
|
||||||
? val.provider.value.url
|
|
||||||
: val.provider.selection
|
|
||||||
|
|
||||||
return this.saveAcme(providerUrl, val.contact)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async addDomain() {}
|
async addDomain() {}
|
||||||
|
|
||||||
async editAcme(provider: string, contact: string[]) {
|
|
||||||
this.formDialog.open(FormComponent, {
|
|
||||||
label: 'Edit ACME Provider',
|
|
||||||
data: {
|
|
||||||
spec: await configBuilderToSpec(this.editAcmeSpec()),
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: this.i18n.transform('Save'),
|
|
||||||
handler: async (
|
|
||||||
val: ReturnType<typeof this.editAcmeSpec>['_TYPE'],
|
|
||||||
) => this.saveAcme(provider, val.contact),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
value: { contact },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeAcme(provider: string) {
|
|
||||||
const loader = this.loader.open('Removing').subscribe()
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.api.removeAcme({ provider })
|
|
||||||
} catch (e: any) {
|
|
||||||
this.errorService.handleError(e)
|
|
||||||
} finally {
|
|
||||||
loader.unsubscribe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async saveAcme(providerUrl: string, contact: string[]) {
|
|
||||||
const loader = this.loader.open('Saving').subscribe()
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.api.initAcme({
|
|
||||||
provider: new URL(providerUrl).href,
|
|
||||||
contact: contact.map(address => `mailto:${address}`),
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
} catch (e: any) {
|
|
||||||
this.errorService.handleError(e)
|
|
||||||
return false
|
|
||||||
} finally {
|
|
||||||
loader.unsubscribe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private addAcmeSpec(providers: string[]) {
|
|
||||||
const availableAcme = knownACME.filter(
|
|
||||||
acme => !providers.includes(acme.url),
|
|
||||||
)
|
|
||||||
|
|
||||||
return ISB.InputSpec.of({
|
|
||||||
provider: ISB.Value.union({
|
|
||||||
name: 'Provider',
|
|
||||||
default: (availableAcme[0]?.url as any) || 'other',
|
|
||||||
variants: ISB.Variants.of({
|
|
||||||
...availableAcme.reduce(
|
|
||||||
(obj, curr) => ({
|
|
||||||
...obj,
|
|
||||||
[curr.url]: {
|
|
||||||
name: curr.name,
|
|
||||||
spec: ISB.InputSpec.of({}),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
other: {
|
|
||||||
name: 'Other',
|
|
||||||
spec: ISB.InputSpec.of({
|
|
||||||
url: ISB.Value.text({
|
|
||||||
name: 'URL',
|
|
||||||
default: null,
|
|
||||||
required: true,
|
|
||||||
inputmode: 'url',
|
|
||||||
patterns: [utils.Patterns.url],
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
contact: this.emailListSpec(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private editAcmeSpec() {
|
|
||||||
return ISB.InputSpec.of({
|
|
||||||
contact: this.emailListSpec(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private emailListSpec() {
|
|
||||||
return ISB.Value.list(
|
|
||||||
ISB.List.text(
|
|
||||||
{
|
|
||||||
name: this.i18n.transform('Contact Emails')!,
|
|
||||||
description: this.i18n.transform(
|
|
||||||
'Needed to obtain a certificate from a Certificate Authority',
|
|
||||||
),
|
|
||||||
minLength: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
inputmode: 'email',
|
|
||||||
patterns: [utils.Patterns.email],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
import {
|
|
||||||
ChangeDetectionStrategy,
|
|
||||||
Component,
|
|
||||||
input,
|
|
||||||
output,
|
|
||||||
} from '@angular/core'
|
|
||||||
import { i18nPipe } from '@start9labs/shared'
|
|
||||||
import {
|
|
||||||
TuiButton,
|
|
||||||
TuiDataList,
|
|
||||||
TuiDropdown,
|
|
||||||
TuiOptGroup,
|
|
||||||
} from '@taiga-ui/core'
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'tr[domain]',
|
|
||||||
template: `
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td>
|
|
||||||
<button
|
|
||||||
tuiIconButton
|
|
||||||
iconStart="@tui.ellipsis"
|
|
||||||
appearance="icon"
|
|
||||||
[tuiDropdown]="content"
|
|
||||||
[(tuiDropdownOpen)]="open"
|
|
||||||
[tuiDropdownMaxHeight]="9999"
|
|
||||||
></button>
|
|
||||||
<ng-template #content>
|
|
||||||
<tui-data-list [style.width.rem]="13">
|
|
||||||
<tui-opt-group>
|
|
||||||
<button
|
|
||||||
tuiOption
|
|
||||||
iconStart="@tui.pencil"
|
|
||||||
(click)="onEdit.emit(domain())"
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
tuiOption
|
|
||||||
iconStart="@tui.shield"
|
|
||||||
(click)="onShowDns.emit(domain())"
|
|
||||||
>
|
|
||||||
Show DNS
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
tuiOption
|
|
||||||
iconStart="@tui.shield"
|
|
||||||
(click)="onTestDns.emit(domain())"
|
|
||||||
>
|
|
||||||
Test DNS
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
tuiOption
|
|
||||||
appearance="negative"
|
|
||||||
iconStart="@tui.trash-2"
|
|
||||||
(click)="onRemove.emit(domain())"
|
|
||||||
>
|
|
||||||
{{ 'Delete' | i18n }}
|
|
||||||
</button>
|
|
||||||
</tui-opt-group>
|
|
||||||
</tui-data-list>
|
|
||||||
</ng-template>
|
|
||||||
</td>
|
|
||||||
`,
|
|
||||||
styles: `
|
|
||||||
td:last-child {
|
|
||||||
grid-area: 3 / span 4;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-align: right;
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host-context(tui-root._mobile) {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(3, min-content) 1fr;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1rem 0.5rem;
|
|
||||||
gap: 0.5rem;
|
|
||||||
|
|
||||||
td {
|
|
||||||
display: flex;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
||||||
imports: [TuiButton, i18nPipe, TuiDropdown, TuiDataList, TuiOptGroup],
|
|
||||||
})
|
|
||||||
export class DomainsItemComponent {
|
|
||||||
readonly domain = input.required<any>()
|
|
||||||
|
|
||||||
onEdit = output<any>()
|
|
||||||
onShowDns = output<any>()
|
|
||||||
onTestDns = output<any>()
|
|
||||||
onRemove = output<any>()
|
|
||||||
|
|
||||||
open = false
|
|
||||||
}
|
|
||||||
@@ -1,110 +1,68 @@
|
|||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
Component,
|
Component,
|
||||||
inject,
|
computed,
|
||||||
input,
|
input,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import {
|
import { i18nPipe } from '@start9labs/shared'
|
||||||
DialogService,
|
|
||||||
ErrorService,
|
|
||||||
i18nPipe,
|
|
||||||
LoadingService,
|
|
||||||
} from '@start9labs/shared'
|
|
||||||
import { ISB } from '@start9labs/start-sdk'
|
|
||||||
import { TuiSkeleton } from '@taiga-ui/kit'
|
import { TuiSkeleton } from '@taiga-ui/kit'
|
||||||
import { filter } from 'rxjs'
|
|
||||||
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
|
||||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
|
||||||
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
|
||||||
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
|
||||||
import { DomainsItemComponent } from './item.component'
|
|
||||||
import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
|
import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
|
||||||
|
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
||||||
|
|
||||||
|
import { DomainsAcmeComponent } from './acme.component'
|
||||||
|
import { DomainsDomainComponent } from './domain.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: '[domains]',
|
selector: 'domains-table',
|
||||||
template: `
|
template: `
|
||||||
<table [appTable]="['Domain', 'Gateway', 'Default ACME', null]">
|
<table [appTable]="titles()">
|
||||||
@for (domain of domains(); track $index) {
|
@for (item of items(); track $index) {
|
||||||
<tr
|
@if (mode() === 'domains') {
|
||||||
[domain]="domain"
|
<tr [domain]="item"></tr>
|
||||||
(onEdit)="edit($event)"
|
} @else if (mode() === 'acme') {
|
||||||
(onShowDns)="showDns($event)"
|
<tr [acme]="item"></tr>
|
||||||
(onTestDns)="testDns($event)"
|
|
||||||
(onRemove)="remove($event)"
|
|
||||||
></tr>
|
|
||||||
} @empty {
|
|
||||||
@if (domains()) {
|
|
||||||
<app-placeholder icon="@tui.award">
|
|
||||||
{{ 'No domains' | i18n }}
|
|
||||||
</app-placeholder>
|
|
||||||
} @else {
|
|
||||||
<tr>
|
|
||||||
<td colspan="5">
|
|
||||||
<div [tuiSkeleton]="true">{{ 'Loading' | i18n }}</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
}
|
||||||
|
} @empty {
|
||||||
|
<tr>
|
||||||
|
<td [attr.colspan]="titles().length">
|
||||||
|
@if (items()) {
|
||||||
|
<app-placeholder icon="@tui.globe">
|
||||||
|
@if (mode() === 'domains') {
|
||||||
|
{{ 'No domains' | i18n }}
|
||||||
|
} @else {
|
||||||
|
{{ 'No saved providers' | i18n }}
|
||||||
|
}
|
||||||
|
</app-placeholder>
|
||||||
|
} @else {
|
||||||
|
<div [tuiSkeleton]="true">{{ 'Loading' | i18n }}</div>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
}
|
}
|
||||||
</table>
|
</table>
|
||||||
`,
|
`,
|
||||||
styles: `
|
|
||||||
:host {
|
|
||||||
grid-column: span 6;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
imports: [
|
||||||
TuiSkeleton,
|
TuiSkeleton,
|
||||||
i18nPipe,
|
i18nPipe,
|
||||||
TableComponent,
|
TableComponent,
|
||||||
DomainsItemComponent,
|
|
||||||
PlaceholderComponent,
|
PlaceholderComponent,
|
||||||
|
DomainsDomainComponent,
|
||||||
|
DomainsAcmeComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class DomainsTableComponent<T extends any> {
|
export class DomainsTableComponent {
|
||||||
readonly domains = input<readonly T[] | null>(null)
|
// @TODO Alex proper types
|
||||||
|
readonly items = input<readonly any[] | null>()
|
||||||
|
readonly mode = input<'domains' | 'acme'>('domains')
|
||||||
|
|
||||||
private readonly dialog = inject(DialogService)
|
readonly titles = computed(() =>
|
||||||
private readonly loader = inject(LoadingService)
|
this.mode() === 'domains'
|
||||||
private readonly errorService = inject(ErrorService)
|
? (['Domain', 'Gateway', 'Default ACME', null] as const)
|
||||||
private readonly api = inject(ApiService)
|
: (['Provider', 'Contact', null] as const),
|
||||||
private readonly formDialog = inject(FormDialogService)
|
)
|
||||||
|
|
||||||
remove(domain: any) {
|
readonly icon = computed(() =>
|
||||||
this.dialog
|
this.mode() === 'domains' ? '@tui.globe' : '@tui.shield-question',
|
||||||
.openConfirm({ label: 'Are you sure?', size: 's' })
|
)
|
||||||
.pipe(filter(Boolean))
|
|
||||||
.subscribe(async () => {
|
|
||||||
const loader = this.loader.open('Deleting').subscribe()
|
|
||||||
|
|
||||||
try {
|
|
||||||
} catch (e: any) {
|
|
||||||
this.errorService.handleError(e)
|
|
||||||
} finally {
|
|
||||||
loader.unsubscribe()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async edit(domain: any) {
|
|
||||||
const renameSpec = ISB.InputSpec.of({})
|
|
||||||
|
|
||||||
this.formDialog.open(FormComponent, {
|
|
||||||
label: 'Edit',
|
|
||||||
data: {
|
|
||||||
spec: await configBuilderToSpec(renameSpec),
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: 'Save',
|
|
||||||
handler: (value: typeof renameSpec._TYPE) => {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async showDns(domain: any) {}
|
|
||||||
|
|
||||||
async testDns(domain: any) {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,14 +40,14 @@ export const SYSTEM_MENU = [
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
icon: '@tui.globe',
|
icon: '@tui.globe',
|
||||||
item: 'Gateways',
|
|
||||||
link: 'gateways',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '@tui.award',
|
|
||||||
item: 'Domains',
|
item: 'Domains',
|
||||||
link: 'domains',
|
link: 'domains',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: '@tui.door-open',
|
||||||
|
item: 'Gateways',
|
||||||
|
link: 'gateways',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user