mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
domains mostly finished
This commit is contained in:
19
web/package-lock.json
generated
19
web/package-lock.json
generated
@@ -62,6 +62,7 @@
|
|||||||
"patch-db-client": "file:../patch-db/client",
|
"patch-db-client": "file:../patch-db/client",
|
||||||
"pbkdf2": "^3.1.2",
|
"pbkdf2": "^3.1.2",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
|
"tldts": "^7.0.11",
|
||||||
"ts-matches": "^6.3.2",
|
"ts-matches": "^6.3.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
@@ -12185,6 +12186,24 @@
|
|||||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tldts": {
|
||||||
|
"version": "7.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.11.tgz",
|
||||||
|
"integrity": "sha512-7k7JV/LZpGhFUu2t+YDaMZ1wdPPRNpaCYNQ0NQbSLY3Rbgy+XbCdkXyqRiS9TLXiYAsrv0yiA0OvnxmgRFCdNA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tldts-core": "^7.0.11"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"tldts": "bin/cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tldts-core": {
|
||||||
|
"version": "7.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.11.tgz",
|
||||||
|
"integrity": "sha512-65eeOpBwWBabh0XqT+zB0vEllq/V3XcrF2fhgMXWWFfNw1yxEjeYg9Vv/B/UNozd0CTR/TohO1ubfn6O6mBW3w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tmp": {
|
"node_modules/tmp": {
|
||||||
"version": "0.0.33",
|
"version": "0.0.33",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||||
|
|||||||
@@ -52,6 +52,7 @@
|
|||||||
"@taiga-ui/addon-table": "4.47.0",
|
"@taiga-ui/addon-table": "4.47.0",
|
||||||
"@taiga-ui/cdk": "4.47.0",
|
"@taiga-ui/cdk": "4.47.0",
|
||||||
"@taiga-ui/core": "4.47.0",
|
"@taiga-ui/core": "4.47.0",
|
||||||
|
"@taiga-ui/dompurify": "4.1.11",
|
||||||
"@taiga-ui/event-plugins": "4.6.0",
|
"@taiga-ui/event-plugins": "4.6.0",
|
||||||
"@taiga-ui/experimental": "4.47.0",
|
"@taiga-ui/experimental": "4.47.0",
|
||||||
"@taiga-ui/icons": "4.47.0",
|
"@taiga-ui/icons": "4.47.0",
|
||||||
@@ -59,7 +60,6 @@
|
|||||||
"@taiga-ui/layout": "4.47.0",
|
"@taiga-ui/layout": "4.47.0",
|
||||||
"@taiga-ui/legacy": "4.47.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",
|
|
||||||
"ansi-to-html": "^0.7.2",
|
"ansi-to-html": "^0.7.2",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
@@ -68,8 +68,8 @@
|
|||||||
"core-js": "^3.42.0",
|
"core-js": "^3.42.0",
|
||||||
"cron": "^2.2.0",
|
"cron": "^2.2.0",
|
||||||
"cronstrue": "^2.21.0",
|
"cronstrue": "^2.21.0",
|
||||||
"dompurify": "^3.1.7",
|
|
||||||
"deep-equality-data-structures": "1.5.1",
|
"deep-equality-data-structures": "1.5.1",
|
||||||
|
"dompurify": "^3.1.7",
|
||||||
"fast-json-patch": "^3.1.1",
|
"fast-json-patch": "^3.1.1",
|
||||||
"fuse.js": "^6.4.6",
|
"fuse.js": "^6.4.6",
|
||||||
"jose": "^4.9.0",
|
"jose": "^4.9.0",
|
||||||
@@ -83,17 +83,18 @@
|
|||||||
"patch-db-client": "file:../patch-db/client",
|
"patch-db-client": "file:../patch-db/client",
|
||||||
"pbkdf2": "^3.1.2",
|
"pbkdf2": "^3.1.2",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
|
"tldts": "^7.0.11",
|
||||||
"ts-matches": "^6.3.2",
|
"ts-matches": "^6.3.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"zone.js": "^0.15.0"
|
"zone.js": "^0.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@angular-experts/hawkeye": "^1.7.2",
|
||||||
"@angular/build": "^20.1.0",
|
"@angular/build": "^20.1.0",
|
||||||
"@angular/cli": "^20.1.0",
|
"@angular/cli": "^20.1.0",
|
||||||
"@angular/compiler-cli": "^20.1.0",
|
"@angular/compiler-cli": "^20.1.0",
|
||||||
"@angular/language-service": "^20.1.0",
|
"@angular/language-service": "^20.1.0",
|
||||||
"@angular-experts/hawkeye": "^1.7.2",
|
|
||||||
"@types/dompurify": "3.0.5",
|
"@types/dompurify": "3.0.5",
|
||||||
"@types/estree": "^0.0.51",
|
"@types/estree": "^0.0.51",
|
||||||
"@types/js-yaml": "^4.0.5",
|
"@types/js-yaml": "^4.0.5",
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ export default {
|
|||||||
109: 'Privat',
|
109: 'Privat',
|
||||||
110: 'Fügen Sie eine Onion-Adresse hinzu, um dieses Interface anonym im Darknet verfügbar zu machen. Onion-Adressen sind nur über das Tor-Netzwerk erreichbar.',
|
110: 'Fügen Sie eine Onion-Adresse hinzu, um dieses Interface anonym im Darknet verfügbar zu machen. Onion-Adressen sind nur über das Tor-Netzwerk erreichbar.',
|
||||||
111: 'Keine Onion-Adressen',
|
111: 'Keine Onion-Adressen',
|
||||||
112: 'Neue Onion-Adresse',
|
112: 'Neue onion-adresse',
|
||||||
113: 'Privater Schlüssel (optional)',
|
113: 'Privater Schlüssel (optional)',
|
||||||
114: 'Optional können Sie einen base64-codierten ed25519-Schlüssel angeben, um die Tor V3 (.onion)-Adresse zu generieren. Wenn nicht angegeben, wird ein zufälliger Schlüssel erstellt.',
|
114: 'Optional können Sie einen base64-codierten ed25519-Schlüssel angeben, um die Tor V3 (.onion)-Adresse zu generieren. Wenn nicht angegeben, wird ein zufälliger Schlüssel erstellt.',
|
||||||
115: 'Verarbeite 10.000 Logs',
|
115: 'Verarbeite 10.000 Logs',
|
||||||
@@ -303,7 +303,7 @@ export default {
|
|||||||
308: 'Erforderlich, um ein Zertifikat von einer Zertifizierungsstelle zu erhalten',
|
308: 'Erforderlich, um ein Zertifikat von einer Zertifizierungsstelle zu erhalten',
|
||||||
309: 'Alle umschalten',
|
309: 'Alle umschalten',
|
||||||
310: 'Fertig',
|
310: 'Fertig',
|
||||||
311: 'Master-Passwort erforderlich',
|
311: 'Master-passwort erforderlich',
|
||||||
312: 'Geben Sie Ihr Master-Passwort ein, um diese Sicherung zu verschlüsseln.',
|
312: 'Geben Sie Ihr Master-Passwort ein, um diese Sicherung zu verschlüsseln.',
|
||||||
313: 'Master-Passwort',
|
313: 'Master-Passwort',
|
||||||
314: 'Master-Passwort eingeben',
|
314: 'Master-Passwort eingeben',
|
||||||
@@ -539,6 +539,5 @@ export default {
|
|||||||
544: 'Domain bearbeiten',
|
544: 'Domain bearbeiten',
|
||||||
545: 'Keine Domains',
|
545: 'Keine Domains',
|
||||||
546: 'Anbieter',
|
546: 'Anbieter',
|
||||||
547: 'DNS anzeigen',
|
547: 'DNS verwalten',
|
||||||
548: 'DNS testen',
|
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const ENGLISH = {
|
|||||||
'Change Password': 13,
|
'Change Password': 13,
|
||||||
'General Settings': 14,
|
'General Settings': 14,
|
||||||
'Manage your overall setup and preferences': 15,
|
'Manage your overall setup and preferences': 15,
|
||||||
'Browser Tab Title': 16,
|
'Browser tab title': 16,
|
||||||
'Language': 17,
|
'Language': 17,
|
||||||
'Disk Repair': 18,
|
'Disk Repair': 18,
|
||||||
'Attempt automatic repair': 19,
|
'Attempt automatic repair': 19,
|
||||||
@@ -103,7 +103,7 @@ export const ENGLISH = {
|
|||||||
'You have unsaved changes. Are you sure you want to leave?': 101,
|
'You have unsaved changes. Are you sure you want to leave?': 101,
|
||||||
'Leave': 102,
|
'Leave': 102,
|
||||||
'Are you sure?': 103,
|
'Are you sure?': 103,
|
||||||
'Select Domain': 104,
|
'Select domain': 104,
|
||||||
'Local': 105,
|
'Local': 105,
|
||||||
'Local addresses can only be accessed by devices connected to the same LAN as your server, either directly or using a VPN.': 106,
|
'Local addresses can only be accessed by devices connected to the same LAN as your server, either directly or using a VPN.': 106,
|
||||||
'Learn More': 107,
|
'Learn More': 107,
|
||||||
@@ -111,7 +111,7 @@ export const ENGLISH = {
|
|||||||
'Private': 109,
|
'Private': 109,
|
||||||
'Add an onion address to anonymously expose this interface on the darknet. Onion addresses can only be reached over the Tor network.': 110,
|
'Add an onion address to anonymously expose this interface on the darknet. Onion addresses can only be reached over the Tor network.': 110,
|
||||||
'No onion addresses': 111,
|
'No onion addresses': 111,
|
||||||
'New Onion Address': 112,
|
'New onion address': 112,
|
||||||
'Private Key (optional)': 113,
|
'Private Key (optional)': 113,
|
||||||
'Optionally provide a base64-encoded ed25519 private key for generating the Tor V3 (.onion) address. If not provided, a random key will be generated and used.': 114,
|
'Optionally provide a base64-encoded ed25519 private key for generating the Tor V3 (.onion) address. If not provided, a random key will be generated and used.': 114,
|
||||||
'Processing 10,000 logs': 115,
|
'Processing 10,000 logs': 115,
|
||||||
@@ -297,16 +297,16 @@ export const ENGLISH = {
|
|||||||
'Contact': 303, // as in, "contact us"
|
'Contact': 303, // as in, "contact us"
|
||||||
'Edit': 304,
|
'Edit': 304,
|
||||||
'Add Certificate Authority': 305,
|
'Add Certificate Authority': 305,
|
||||||
'Edit Contact Info': 306,
|
'Edit contact info': 306,
|
||||||
'Contact Emails': 307,
|
'Contact Emails': 307,
|
||||||
'Needed to obtain a certificate from a Certificate Authority': 308,
|
'Needed to obtain a certificate from a Certificate Authority': 308,
|
||||||
'Toggle all': 309,
|
'Toggle all': 309,
|
||||||
'Done': 310,
|
'Done': 310,
|
||||||
'Master Password Needed': 311,
|
'Master password needed': 311,
|
||||||
'Enter your master password to encrypt this backup.': 312,
|
'Enter your master password to encrypt this backup.': 312,
|
||||||
'Master Password': 313,
|
'Master Password': 313,
|
||||||
'Enter master password': 314,
|
'Enter master password': 314,
|
||||||
'Original Password Needed': 315,
|
'Original password needed': 315,
|
||||||
'This backup was created with a different password. Enter the original password that was used to encrypt this backup.': 316,
|
'This backup was created with a different password. Enter the original password that was used to encrypt this backup.': 316,
|
||||||
'Original Password': 317,
|
'Original Password': 317,
|
||||||
'Enter original password': 318,
|
'Enter original password': 318,
|
||||||
@@ -363,7 +363,7 @@ export const ENGLISH = {
|
|||||||
'Ready to restore': 369,
|
'Ready to restore': 369,
|
||||||
'Local Hostname': 370,
|
'Local Hostname': 370,
|
||||||
'Created': 371,
|
'Created': 371,
|
||||||
'Password Required': 372,
|
'Password required': 372,
|
||||||
'Enter the master password that was used to encrypt this backup. On the next screen, you will select the individual services you want to restore.': 373,
|
'Enter the master password that was used to encrypt this backup. On the next screen, you will select the individual services you want to restore.': 373,
|
||||||
'Decrypting drive': 374,
|
'Decrypting drive': 374,
|
||||||
'Select services to restore': 375,
|
'Select services to restore': 375,
|
||||||
@@ -395,7 +395,7 @@ export const ENGLISH = {
|
|||||||
'Terminate selected': 401,
|
'Terminate selected': 401,
|
||||||
'Terminating sessions': 402,
|
'Terminating sessions': 402,
|
||||||
'No sessions': 403,
|
'No sessions': 403,
|
||||||
'Password Needed': 404,
|
'Password needed': 404,
|
||||||
'Connected': 405,
|
'Connected': 405,
|
||||||
'Forget': 406, // as in, delete or remove
|
'Forget': 406, // as in, delete or remove
|
||||||
'WiFi Credentials': 407,
|
'WiFi Credentials': 407,
|
||||||
@@ -526,7 +526,7 @@ export const ENGLISH = {
|
|||||||
'Finished': 532, // an in, complete
|
'Finished': 532, // an in, complete
|
||||||
'Gateways': 533, // as in, a device or software that connects two different networks
|
'Gateways': 533, // as in, a device or software that connects two different networks
|
||||||
'Gateways connect your server to the Internet. They process outbound traffic, and under certain conditions, they also permit inbound traffic.': 534,
|
'Gateways connect your server to the Internet. They process outbound traffic, and under certain conditions, they also permit inbound traffic.': 534,
|
||||||
'Add Gateway': 535, // as in, add a new network gateway to StartOS
|
'Add gateway': 535, // as in, add a new network gateway to StartOS
|
||||||
'Rename': 536,
|
'Rename': 536,
|
||||||
'Access': 537, // as in, public or private access, almost "permission"
|
'Access': 537, // as in, public or private access, almost "permission"
|
||||||
'Domains': 538, // as in, internet domains
|
'Domains': 538, // as in, internet domains
|
||||||
@@ -535,9 +535,8 @@ export const ENGLISH = {
|
|||||||
'Gateway': 541, // as in, a device or software that connects two different networks
|
'Gateway': 541, // as in, a device or software that connects two different networks
|
||||||
'Default Certificate Authority': 542,
|
'Default Certificate Authority': 542,
|
||||||
'Certificate Authority': 543,
|
'Certificate Authority': 543,
|
||||||
'Edit Domain': 544,
|
'Edit domain': 544,
|
||||||
'No domains': 545,
|
'No domains': 545,
|
||||||
'Provider': 546,
|
'Provider': 546,
|
||||||
'Show DNS': 547,
|
'Manage DNS': 547,
|
||||||
'Test DNS': 548,
|
|
||||||
} as const
|
} as const
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ export default {
|
|||||||
109: 'Privado',
|
109: 'Privado',
|
||||||
110: 'Agrega una dirección onion para exponer esta interfaz de forma anónima en la darknet. Las direcciones onion solo se pueden acceder a través de la red Tor.',
|
110: 'Agrega una dirección onion para exponer esta interfaz de forma anónima en la darknet. Las direcciones onion solo se pueden acceder a través de la red Tor.',
|
||||||
111: 'Sin direcciones onion',
|
111: 'Sin direcciones onion',
|
||||||
112: 'Nueva dirección Onion',
|
112: 'Nueva dirección onion',
|
||||||
113: 'Clave privada (opcional)',
|
113: 'Clave privada (opcional)',
|
||||||
114: 'Opcionalmente proporciona una clave privada ed25519 codificada en base64 para generar la dirección Tor V3 (.onion). Si no se proporciona, se generará una clave aleatoria.',
|
114: 'Opcionalmente proporciona una clave privada ed25519 codificada en base64 para generar la dirección Tor V3 (.onion). Si no se proporciona, se generará una clave aleatoria.',
|
||||||
115: 'Procesando 10,000 registros',
|
115: 'Procesando 10,000 registros',
|
||||||
@@ -294,7 +294,6 @@ export default {
|
|||||||
297: 'Se detectó un paquete s9pk de versión 1. Este formato está obsoleto. Puedes instalarlo manualmente con start-cli si es necesario.',
|
297: 'Se detectó un paquete s9pk de versión 1. Este formato está obsoleto. Puedes instalarlo manualmente con start-cli si es necesario.',
|
||||||
298: 'Archivo de paquete inválido',
|
298: 'Archivo de paquete inválido',
|
||||||
299: 'Agregar un dominio a StartOS significa que puedes usarlo y sus subdominios para alojar interfaces de servicios en Internet público.',
|
299: 'Agregar un dominio a StartOS significa que puedes usarlo y sus subdominios para alojar interfaces de servicios en Internet público.',
|
||||||
|
|
||||||
300: 'Ver instrucciones',
|
300: 'Ver instrucciones',
|
||||||
303: 'Contacto',
|
303: 'Contacto',
|
||||||
304: 'Editar',
|
304: 'Editar',
|
||||||
@@ -540,6 +539,5 @@ export default {
|
|||||||
544: 'Editar dominio',
|
544: 'Editar dominio',
|
||||||
545: 'Sin dominios',
|
545: 'Sin dominios',
|
||||||
546: 'Proveedor',
|
546: 'Proveedor',
|
||||||
547: 'Mostrar DNS',
|
547: 'Administrar DNS',
|
||||||
548: 'Probar DNS',
|
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -539,6 +539,5 @@ export default {
|
|||||||
544: 'Modifier le domaine',
|
544: 'Modifier le domaine',
|
||||||
545: 'Aucun domaine',
|
545: 'Aucun domaine',
|
||||||
546: 'Fournisseur',
|
546: 'Fournisseur',
|
||||||
547: 'Afficher le DNS',
|
547: 'Gérer le DNS',
|
||||||
548: 'Tester le DNS',
|
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ export default {
|
|||||||
109: 'Prywatny',
|
109: 'Prywatny',
|
||||||
110: 'Dodaj adres onion, aby anonimowo udostępnić ten interfejs w sieci Tor. Adresy onion są dostępne tylko przez sieć Tor.',
|
110: 'Dodaj adres onion, aby anonimowo udostępnić ten interfejs w sieci Tor. Adresy onion są dostępne tylko przez sieć Tor.',
|
||||||
111: 'Brak adresów onion',
|
111: 'Brak adresów onion',
|
||||||
112: 'Nowy adres Onion',
|
112: 'Nowy adres onion',
|
||||||
113: 'Klucz prywatny (opcjonalnie)',
|
113: 'Klucz prywatny (opcjonalnie)',
|
||||||
114: 'Opcjonalnie podaj klucz prywatny ed25519 zakodowany w base64, aby wygenerować adres Tor V3 (.onion). Jeśli nie zostanie podany, zostanie wygenerowany i użyty losowy klucz.',
|
114: 'Opcjonalnie podaj klucz prywatny ed25519 zakodowany w base64, aby wygenerować adres Tor V3 (.onion). Jeśli nie zostanie podany, zostanie wygenerowany i użyty losowy klucz.',
|
||||||
115: 'Przetwarzanie 10 000 logów',
|
115: 'Przetwarzanie 10 000 logów',
|
||||||
@@ -539,6 +539,5 @@ export default {
|
|||||||
544: 'Edytuj domenę',
|
544: 'Edytuj domenę',
|
||||||
545: 'Brak domen',
|
545: 'Brak domen',
|
||||||
546: 'Dostawca',
|
546: 'Dostawca',
|
||||||
547: 'Pokaż DNS',
|
547: 'Zarządzaj DNS',
|
||||||
548: 'Test DNS',
|
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import { distinctUntilChanged, map, merge, Subject } from 'rxjs'
|
|||||||
import { ConfigService } from 'src/app/services/config.service'
|
import { ConfigService } from 'src/app/services/config.service'
|
||||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||||
|
|
||||||
|
// @TODO translations
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'refresh-alert',
|
selector: 'refresh-alert',
|
||||||
template: `
|
template: `
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ export class InterfaceClearnetComponent {
|
|||||||
host: this.interface.value().addressInfo.hostId,
|
host: this.interface.value().addressInfo.hostId,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
await this.api.serverRemoveDomain(params)
|
await this.api.osUiRemoveDomain(params)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@@ -256,7 +256,7 @@ export class InterfaceClearnetComponent {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.formDialog.open<FormContext<ClearnetForm>>(FormComponent, {
|
this.formDialog.open<FormContext<ClearnetForm>>(FormComponent, {
|
||||||
label: 'Select Domain',
|
label: 'Select domain',
|
||||||
data: {
|
data: {
|
||||||
spec: await configBuilderToSpec(
|
spec: await configBuilderToSpec(
|
||||||
ISB.InputSpec.of(
|
ISB.InputSpec.of(
|
||||||
@@ -292,7 +292,7 @@ export class InterfaceClearnetComponent {
|
|||||||
host: this.interface.value().addressInfo.hostId,
|
host: this.interface.value().addressInfo.hostId,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
await this.api.serverAddDomain(params)
|
await this.api.osUiAddDomain(params)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ export class InterfaceTorComponent {
|
|||||||
|
|
||||||
async add() {
|
async add() {
|
||||||
this.formDialog.open<FormContext<OnionForm>>(FormComponent, {
|
this.formDialog.open<FormContext<OnionForm>>(FormComponent, {
|
||||||
label: 'New Onion Address',
|
label: 'New onion address',
|
||||||
data: {
|
data: {
|
||||||
spec: await configBuilderToSpec(
|
spec: await configBuilderToSpec(
|
||||||
ISB.InputSpec.of({
|
ISB.InputSpec.of({
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ export class TabsComponent {
|
|||||||
)
|
)
|
||||||
|
|
||||||
more(content: TemplateRef<any>) {
|
more(content: TemplateRef<any>) {
|
||||||
this.dialogs.open(content, { label: 'Start OS' }).subscribe({
|
this.dialogs.open(content, { label: 'StartOS' }).subscribe({
|
||||||
complete: () => this.update(),
|
complete: () => this.update(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export class BackupsBackupComponent {
|
|||||||
|
|
||||||
this.dialog
|
this.dialog
|
||||||
.openPrompt<string>({
|
.openPrompt<string>({
|
||||||
label: 'Master Password Needed',
|
label: 'Master password needed',
|
||||||
data: {
|
data: {
|
||||||
message: 'Enter your master password to encrypt this backup.',
|
message: 'Enter your master password to encrypt this backup.',
|
||||||
label: 'Master Password',
|
label: 'Master Password',
|
||||||
@@ -169,7 +169,7 @@ export class BackupsBackupComponent {
|
|||||||
|
|
||||||
this.dialog
|
this.dialog
|
||||||
.openPrompt<string>({
|
.openPrompt<string>({
|
||||||
label: 'Original Password Needed',
|
label: 'Original password needed',
|
||||||
data: {
|
data: {
|
||||||
message:
|
message:
|
||||||
'This backup was created with a different password. Enter the original password that was used to encrypt this backup.',
|
'This backup was created with a different password. Enter the original password that was used to encrypt this backup.',
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export class BackupRestoreComponent {
|
|||||||
onClick(serverId: string, { passwordHash }: StartOSDiskInfo) {
|
onClick(serverId: string, { passwordHash }: StartOSDiskInfo) {
|
||||||
this.dialog
|
this.dialog
|
||||||
.openPrompt<string>({
|
.openPrompt<string>({
|
||||||
label: 'Password Required',
|
label: 'Password required',
|
||||||
data: {
|
data: {
|
||||||
message:
|
message:
|
||||||
'Enter the master password that was used to encrypt this backup. On the next screen, you will select the individual services you want to restore.',
|
'Enter the master password that was used to encrypt this backup. On the next screen, you will select the individual services you want to restore.',
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export class AuthorityService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.formDialog.open(FormComponent, {
|
this.formDialog.open(FormComponent, {
|
||||||
label: 'Edit Contact Info',
|
label: 'Edit contact info',
|
||||||
data: {
|
data: {
|
||||||
spec: await configBuilderToSpec(editSpec),
|
spec: await configBuilderToSpec(editSpec),
|
||||||
buttons: [
|
buttons: [
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ import { Authority, AuthorityService } from './authority.service'
|
|||||||
@if (authority(); as authority) {
|
@if (authority(); as authority) {
|
||||||
<td>{{ authority.name }}</td>
|
<td>{{ authority.name }}</td>
|
||||||
<td>{{ authority.url || '-' }}</td>
|
<td>{{ authority.url || '-' }}</td>
|
||||||
<td>{{ authority.contact ? authority.contact.join(', ') : '-' }}</td>
|
<td class="hidden">
|
||||||
|
{{ authority.contact ? authority.contact.join(', ') : '-' }}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button
|
<button
|
||||||
tuiIconButton
|
tuiIconButton
|
||||||
@@ -73,7 +75,7 @@ import { Authority, AuthorityService } from './authority.service'
|
|||||||
`,
|
`,
|
||||||
styles: `
|
styles: `
|
||||||
td:last-child {
|
td:last-child {
|
||||||
grid-area: 1 / 2 / 3;
|
grid-area: 1 / 2 / 4;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
@@ -85,6 +87,10 @@ import { Authority, AuthorityService } from './authority.service'
|
|||||||
font: var(--tui-font-text-m);
|
font: var(--tui-font-text-m);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||||
|
import { ErrorService, i18nPipe, LoadingService } from '@start9labs/shared'
|
||||||
|
import { TuiButton, TuiDialogContext } from '@taiga-ui/core'
|
||||||
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
|
import { MappedDomain } from './domain.service'
|
||||||
|
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||||
|
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'dns',
|
||||||
|
template: `
|
||||||
|
<section class="g-card">
|
||||||
|
<header>
|
||||||
|
{{ $any('Using IP') | i18n }}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
@let subdomain = context.data.subdomain;
|
||||||
|
@let wanIp = context.data.gateway.ipInfo?.wanIp || ('Error' | i18n);
|
||||||
|
|
||||||
|
<table [appTable]="['Type', $any('Host'), 'Value', 'Purpose']">
|
||||||
|
<tr>
|
||||||
|
<td>A</td>
|
||||||
|
<td>{{ subdomain || '@' }}</td>
|
||||||
|
<td>{{ wanIp }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>A</td>
|
||||||
|
<td>
|
||||||
|
{{ subdomain ? '*.' + subdomain : '*' }}
|
||||||
|
</td>
|
||||||
|
<td>{{ wanIp }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
@if (context.data.gateway.ipInfo?.deviceType !== 'wireguard') {
|
||||||
|
<section class="g-card">
|
||||||
|
<header>
|
||||||
|
{{ $any('Using Dynamic DNS') | i18n }}
|
||||||
|
</header>
|
||||||
|
<table [appTable]="['Type', $any('Host'), 'Value', 'Purpose']">
|
||||||
|
<tr>
|
||||||
|
<td>ALIAS</td>
|
||||||
|
<td>{{ subdomain || '@' }}</td>
|
||||||
|
<td>[Dynamic DNS Address]</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ALIAS</td>
|
||||||
|
<td>{{ subdomain ? '*.' + subdomain : '*' }}</td>
|
||||||
|
<td>[Dynamic DNS Address]</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
|
||||||
|
<button tuiButton size="l" (click)="testDns()">
|
||||||
|
{{ 'Test' | i18n }}
|
||||||
|
</button>
|
||||||
|
`,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [TuiButton, i18nPipe, TableComponent],
|
||||||
|
})
|
||||||
|
export class DnsComponent {
|
||||||
|
private readonly loader = inject(LoadingService)
|
||||||
|
private readonly errorService = inject(ErrorService)
|
||||||
|
private readonly api = inject(ApiService)
|
||||||
|
|
||||||
|
readonly context = injectContext<TuiDialogContext<void, MappedDomain>>()
|
||||||
|
|
||||||
|
async testDns() {
|
||||||
|
const loader = this.loader.open().subscribe()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.api.testDomain({
|
||||||
|
fqdn: this.context.data.fqdn,
|
||||||
|
gateway: this.context.data.gateway.id,
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
} catch (e: any) {
|
||||||
|
this.errorService.handleError(e)
|
||||||
|
return false
|
||||||
|
} finally {
|
||||||
|
loader.unsubscribe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DNS = new PolymorpheusComponent(DnsComponent)
|
||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
LoadingService,
|
LoadingService,
|
||||||
} from '@start9labs/shared'
|
} from '@start9labs/shared'
|
||||||
import { toSignal } from '@angular/core/rxjs-interop'
|
import { toSignal } from '@angular/core/rxjs-interop'
|
||||||
import { ISB, utils } from '@start9labs/start-sdk'
|
import { ISB, T, utils } from '@start9labs/start-sdk'
|
||||||
import { filter, map } from 'rxjs'
|
import { filter, map } from 'rxjs'
|
||||||
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
@@ -15,9 +15,26 @@ import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
|||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||||
import { toAuthorityName } from 'src/app/utils/acme'
|
import { toAuthorityName } from 'src/app/utils/acme'
|
||||||
|
import { parse } from 'tldts'
|
||||||
|
import { RR } from 'src/app/services/api/api.types'
|
||||||
|
import { DNS } from './dns.component'
|
||||||
|
|
||||||
// @TODO translations
|
// @TODO translations
|
||||||
|
|
||||||
|
export type MappedDomain = {
|
||||||
|
fqdn: string
|
||||||
|
subdomain: string | null
|
||||||
|
gateway: {
|
||||||
|
id: string
|
||||||
|
name: string | null
|
||||||
|
ipInfo: T.IpInfo | null
|
||||||
|
}
|
||||||
|
authority: {
|
||||||
|
url: string | null
|
||||||
|
name: string | null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DomainService {
|
export class DomainService {
|
||||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||||
@@ -30,57 +47,45 @@ export class DomainService {
|
|||||||
|
|
||||||
readonly data = toSignal(
|
readonly data = toSignal(
|
||||||
this.patch.watch$('serverInfo', 'network').pipe(
|
this.patch.watch$('serverInfo', 'network').pipe(
|
||||||
map(network => {
|
map(({ networkInterfaces, domains, acme }) => ({
|
||||||
return {
|
gateways: Object.entries(networkInterfaces).reduce<
|
||||||
gateways: Object.entries(network.networkInterfaces).reduce<
|
Record<string, string>
|
||||||
Record<string, string>
|
>(
|
||||||
>(
|
(obj, [id, n]) => ({
|
||||||
(obj, [id, n]) => ({
|
...obj,
|
||||||
...obj,
|
[id]: n.ipInfo?.name || '',
|
||||||
[id]: n.ipInfo?.name || '',
|
}),
|
||||||
}),
|
{},
|
||||||
{},
|
),
|
||||||
),
|
domains: Object.entries(domains).map(
|
||||||
// @TODO use real data
|
([fqdn, { gateway, acme }]) =>
|
||||||
domains: [
|
({
|
||||||
{
|
fqdn,
|
||||||
domain: 'blog.mydomain.com',
|
subdomain: parse(fqdn).subdomain,
|
||||||
gateway: {
|
gateway: {
|
||||||
id: 'wireguard1',
|
id: gateway,
|
||||||
name: 'StartTunnel',
|
ipInfo: networkInterfaces[gateway]?.ipInfo || null,
|
||||||
},
|
},
|
||||||
authority: {
|
authority: {
|
||||||
url: 'https://acme-v02.api.letsencrypt.org/directory',
|
url: acme,
|
||||||
name: `Let's Encrypt`,
|
name: toAuthorityName(acme),
|
||||||
},
|
},
|
||||||
},
|
}) as MappedDomain,
|
||||||
{
|
),
|
||||||
domain: 'store.mydomain.com',
|
authorities: Object.keys(acme).reduce<Record<string, string>>(
|
||||||
gateway: {
|
(obj, url) => ({
|
||||||
id: 'eth0',
|
...obj,
|
||||||
name: 'Ethernet',
|
[url]: toAuthorityName(url),
|
||||||
},
|
}),
|
||||||
authority: {
|
{ local: toAuthorityName(null) },
|
||||||
url: 'local',
|
),
|
||||||
name: toAuthorityName(null),
|
})),
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
authorities: Object.keys(network.acme).reduce<Record<string, string>>(
|
|
||||||
(obj, url) => ({
|
|
||||||
...obj,
|
|
||||||
[url]: toAuthorityName(url),
|
|
||||||
}),
|
|
||||||
{ local: toAuthorityName(null) },
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
async add() {
|
async add() {
|
||||||
const addSpec = ISB.InputSpec.of({
|
const addSpec = ISB.InputSpec.of({
|
||||||
domain: ISB.Value.text({
|
fqdn: ISB.Value.text({
|
||||||
name: 'Domain',
|
name: 'Domain',
|
||||||
description:
|
description:
|
||||||
'Enter a domain/subdomain. For example, if you control domain.com, you could enter domain.com or subdomain.domain.com or another.subdomain.domain.com. In any case, the domain you enter and all possible subdomains of the domain will be available for assignment in StartOS',
|
'Enter a domain/subdomain. For example, if you control domain.com, you could enter domain.com or subdomain.domain.com or another.subdomain.domain.com. In any case, the domain you enter and all possible subdomains of the domain will be available for assignment in StartOS',
|
||||||
@@ -92,26 +97,31 @@ export class DomainService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.formDialog.open(FormComponent, {
|
this.formDialog.open(FormComponent, {
|
||||||
label: 'Add Domain' as any,
|
label: 'Add domain',
|
||||||
data: {
|
data: {
|
||||||
spec: await configBuilderToSpec(addSpec),
|
spec: await configBuilderToSpec(addSpec),
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
text: 'Save',
|
text: 'Save',
|
||||||
handler: (input: typeof addSpec._TYPE) => this.save(input),
|
handler: (input: typeof addSpec._TYPE) =>
|
||||||
|
this.save({
|
||||||
|
fqdn: input.fqdn,
|
||||||
|
gateway: input.gateway,
|
||||||
|
acme: input.authority === 'local' ? null : input.authority,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async edit(domain: any) {
|
async edit(domain: MappedDomain) {
|
||||||
const editSpec = ISB.InputSpec.of({
|
const editSpec = ISB.InputSpec.of({
|
||||||
...this.gatewaysAndAuthorities(),
|
...this.gatewaysAndAuthorities(),
|
||||||
})
|
})
|
||||||
|
|
||||||
this.formDialog.open(FormComponent, {
|
this.formDialog.open(FormComponent, {
|
||||||
label: 'Edit Domain',
|
label: 'Edit domain',
|
||||||
data: {
|
data: {
|
||||||
spec: await configBuilderToSpec(editSpec),
|
spec: await configBuilderToSpec(editSpec),
|
||||||
buttons: [
|
buttons: [
|
||||||
@@ -119,20 +129,21 @@ export class DomainService {
|
|||||||
text: 'Save',
|
text: 'Save',
|
||||||
handler: (input: typeof editSpec._TYPE) =>
|
handler: (input: typeof editSpec._TYPE) =>
|
||||||
this.save({
|
this.save({
|
||||||
domain: domain.domain,
|
fqdn: domain.fqdn,
|
||||||
...input,
|
gateway: input.gateway,
|
||||||
|
acme: input.authority === 'local' ? null : input.authority,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
value: {
|
value: {
|
||||||
gateway: domain.gateway.id,
|
gateway: domain.gateway.id,
|
||||||
authority: domain.authority.url,
|
authority: domain.authority.url || 'local',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(domain: any) {
|
remove(fqdn: string) {
|
||||||
this.dialog
|
this.dialog
|
||||||
.openConfirm({ label: 'Are you sure?', size: 's' })
|
.openConfirm({ label: 'Are you sure?', size: 's' })
|
||||||
.pipe(filter(Boolean))
|
.pipe(filter(Boolean))
|
||||||
@@ -140,7 +151,7 @@ export class DomainService {
|
|||||||
const loader = this.loader.open('Deleting').subscribe()
|
const loader = this.loader.open('Deleting').subscribe()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// @TODO API
|
await this.api.removeDomain({ fqdn })
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
this.errorService.handleError(e)
|
this.errorService.handleError(e)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -149,20 +160,17 @@ export class DomainService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
showDns(domain: any) {
|
showDns(domain: MappedDomain) {
|
||||||
// @TODO
|
this.dialog
|
||||||
|
.openComponent(DNS, { label: 'Manage DNS', data: domain })
|
||||||
|
.subscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
testDns(domain: any) {
|
private async save(params: RR.AddDomainReq) {
|
||||||
// @TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
// @TODO different endpoints for create and edit?
|
|
||||||
private async save(params: any) {
|
|
||||||
const loader = this.loader.open('Saving').subscribe()
|
const loader = this.loader.open('Saving').subscribe()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// @TODO API
|
await this.api.addDomain(params)
|
||||||
return true
|
return true
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
this.errorService.handleError(e)
|
this.errorService.handleError(e)
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ import {
|
|||||||
TuiDropdown,
|
TuiDropdown,
|
||||||
TuiTextfield,
|
TuiTextfield,
|
||||||
} from '@taiga-ui/core'
|
} from '@taiga-ui/core'
|
||||||
import { DomainService } from './domain.service'
|
import { DomainService, MappedDomain } from './domain.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tr[domain]',
|
selector: 'tr[domain]',
|
||||||
template: `
|
template: `
|
||||||
@if (domain(); as domain) {
|
@if (domain(); as domain) {
|
||||||
<td>{{ domain.domain }}</td>
|
<td>{{ domain.fqdn }}</td>
|
||||||
<td [style.order]="-1">{{ domain.gateway.name }}</td>
|
<td [style.order]="-1">{{ domain.gateway.ipInfo?.name || '-' }}</td>
|
||||||
<td>{{ domain.authority.name }}</td>
|
<td>{{ domain.authority.name }}</td>
|
||||||
<td>
|
<td>
|
||||||
<button
|
<button
|
||||||
@@ -47,15 +47,7 @@ import { DomainService } from './domain.service'
|
|||||||
iconStart="@tui.eye"
|
iconStart="@tui.eye"
|
||||||
(click)="domainService.showDns(domain)"
|
(click)="domainService.showDns(domain)"
|
||||||
>
|
>
|
||||||
{{ 'Show DNS' | i18n }}
|
{{ 'Manage DNS' | i18n }}
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
tuiOption
|
|
||||||
new
|
|
||||||
iconStart="@tui.arrow-up-down"
|
|
||||||
(click)="domainService.testDns(domain)"
|
|
||||||
>
|
|
||||||
{{ 'Test DNS' | i18n }}
|
|
||||||
</button>
|
</button>
|
||||||
</tui-opt-group>
|
</tui-opt-group>
|
||||||
<tui-opt-group>
|
<tui-opt-group>
|
||||||
@@ -64,7 +56,7 @@ import { DomainService } from './domain.service'
|
|||||||
new
|
new
|
||||||
iconStart="@tui.trash"
|
iconStart="@tui.trash"
|
||||||
class="g-negative"
|
class="g-negative"
|
||||||
(click)="domainService.remove(domain)"
|
(click)="domainService.remove(domain.fqdn)"
|
||||||
>
|
>
|
||||||
{{ 'Delete' | i18n }}
|
{{ 'Delete' | i18n }}
|
||||||
</button>
|
</button>
|
||||||
@@ -96,7 +88,7 @@ import { DomainService } from './domain.service'
|
|||||||
export class DomainItemComponent {
|
export class DomainItemComponent {
|
||||||
protected readonly domainService = inject(DomainService)
|
protected readonly domainService = inject(DomainService)
|
||||||
|
|
||||||
readonly domain = input.required<any>()
|
readonly domain = input.required<MappedDomain>()
|
||||||
|
|
||||||
open = false
|
open = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { TitleDirective } from 'src/app/services/title.service'
|
|||||||
import { TuiHeader } from '@taiga-ui/layout'
|
import { TuiHeader } from '@taiga-ui/layout'
|
||||||
import { map } from 'rxjs'
|
import { map } from 'rxjs'
|
||||||
import { ISB } from '@start9labs/start-sdk'
|
import { ISB } from '@start9labs/start-sdk'
|
||||||
import { GatewayWithID } from './item.component'
|
import { GatewayPlus } from './item.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
@@ -87,19 +87,24 @@ export default class GatewaysComponent {
|
|||||||
.watch$('serverInfo', 'network', 'networkInterfaces')
|
.watch$('serverInfo', 'network', 'networkInterfaces')
|
||||||
.pipe(
|
.pipe(
|
||||||
map(gateways =>
|
map(gateways =>
|
||||||
Object.entries(gateways).map(
|
Object.entries(gateways)
|
||||||
([id, val]) =>
|
.filter(([_, val]) => !!val.ipInfo)
|
||||||
({
|
.map(
|
||||||
...val,
|
([id, val]) =>
|
||||||
id,
|
({
|
||||||
}) as GatewayWithID,
|
...val,
|
||||||
),
|
id,
|
||||||
|
ipv4: val.ipInfo?.subnets
|
||||||
|
.filter(s => !s.includes('::'))
|
||||||
|
.map(s => s.split('/')[0]),
|
||||||
|
}) as GatewayPlus,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
async add() {
|
async add() {
|
||||||
this.formDialog.open(FormComponent, {
|
this.formDialog.open(FormComponent, {
|
||||||
label: 'Add Gateway',
|
label: 'Add gateway',
|
||||||
data: {
|
data: {
|
||||||
spec: await configBuilderToSpec(gatewaySpec),
|
spec: await configBuilderToSpec(gatewaySpec),
|
||||||
buttons: [
|
buttons: [
|
||||||
|
|||||||
@@ -24,55 +24,64 @@ import { ApiService } from 'src/app/services/api/embassy-api.service'
|
|||||||
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
import { FormDialogService } from 'src/app/services/form-dialog.service'
|
||||||
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
|
||||||
|
|
||||||
export type GatewayWithID = T.NetworkInterfaceInfo & {
|
export type GatewayPlus = T.NetworkInterfaceInfo & {
|
||||||
id: string
|
id: string
|
||||||
ipInfo: T.IpInfo
|
ipInfo: T.IpInfo
|
||||||
|
ipv4: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tr[proxy]',
|
selector: 'tr[proxy]',
|
||||||
template: `
|
template: `
|
||||||
<td [style.grid-column]="'span 2'">{{ proxy().ipInfo.name }}</td>
|
@if (proxy(); as proxy) {
|
||||||
<td class="type">{{ proxy().ipInfo.deviceType || '-' }}</td>
|
<td [style.grid-column]="'span 2'">{{ proxy.ipInfo.name }}</td>
|
||||||
<td [style.order]="-2">
|
<td class="type">{{ proxy.ipInfo.deviceType || '-' }}</td>
|
||||||
{{ proxy().public ? ('Public' | i18n) : ('Private' | i18n) }}
|
<td [style.order]="-2">
|
||||||
</td>
|
{{ proxy.public ? ('Public' | i18n) : ('Private' | i18n) }}
|
||||||
<!-- // @TODO show both LAN IPs? -->
|
</td>
|
||||||
<td class="lan">{{ proxy().ipInfo.subnets[0] }}</td>
|
<td class="lan">{{ proxy.ipv4.join(', ') }}</td>
|
||||||
<td class="wan">{{ proxy().ipInfo.wanIp }}</td>
|
<td
|
||||||
<td>
|
class="wan"
|
||||||
<button
|
[style.color]="
|
||||||
tuiIconButton
|
proxy.ipInfo.wanIp ? 'var(--tui-text-warning)' : undefined
|
||||||
tuiDropdown
|
"
|
||||||
size="s"
|
|
||||||
appearance="flat-grayscale"
|
|
||||||
iconStart="@tui.ellipsis-vertical"
|
|
||||||
[tuiAppearanceState]="open ? 'hover' : null"
|
|
||||||
[(tuiDropdownOpen)]="open"
|
|
||||||
>
|
>
|
||||||
{{ 'More' | i18n }}
|
{{ proxy.ipInfo.wanIp || ('Error' | i18n) }}
|
||||||
<tui-data-list size="s" *tuiTextfieldDropdown>
|
</td>
|
||||||
<tui-opt-group>
|
<td>
|
||||||
<button tuiOption new iconStart="@tui.pencil" (click)="rename()">
|
<button
|
||||||
{{ 'Rename' | i18n }}
|
tuiIconButton
|
||||||
</button>
|
tuiDropdown
|
||||||
</tui-opt-group>
|
size="s"
|
||||||
@if (proxy().ipInfo.deviceType === 'wireguard') {
|
appearance="flat-grayscale"
|
||||||
|
iconStart="@tui.ellipsis-vertical"
|
||||||
|
[tuiAppearanceState]="open ? 'hover' : null"
|
||||||
|
[(tuiDropdownOpen)]="open"
|
||||||
|
>
|
||||||
|
{{ 'More' | i18n }}
|
||||||
|
<tui-data-list size="s" *tuiTextfieldDropdown>
|
||||||
<tui-opt-group>
|
<tui-opt-group>
|
||||||
<button
|
<button tuiOption new iconStart="@tui.pencil" (click)="rename()">
|
||||||
tuiOption
|
{{ 'Rename' | i18n }}
|
||||||
new
|
|
||||||
iconStart="@tui.trash"
|
|
||||||
class="g-negative"
|
|
||||||
(click)="remove()"
|
|
||||||
>
|
|
||||||
{{ 'Delete' | i18n }}
|
|
||||||
</button>
|
</button>
|
||||||
</tui-opt-group>
|
</tui-opt-group>
|
||||||
}
|
@if (proxy.ipInfo.deviceType === 'wireguard') {
|
||||||
</tui-data-list>
|
<tui-opt-group>
|
||||||
</button>
|
<button
|
||||||
</td>
|
tuiOption
|
||||||
|
new
|
||||||
|
iconStart="@tui.trash"
|
||||||
|
class="g-negative"
|
||||||
|
(click)="remove()"
|
||||||
|
>
|
||||||
|
{{ 'Delete' | i18n }}
|
||||||
|
</button>
|
||||||
|
</tui-opt-group>
|
||||||
|
}
|
||||||
|
</tui-data-list>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
styles: `
|
styles: `
|
||||||
td:last-child {
|
td:last-child {
|
||||||
@@ -106,7 +115,7 @@ export type GatewayWithID = T.NetworkInterfaceInfo & {
|
|||||||
grid-column: span 2;
|
grid-column: span 2;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: 'LAN IPs: ';
|
content: 'LAN IP: ';
|
||||||
color: var(--tui-text-primary);
|
color: var(--tui-text-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,7 +142,7 @@ export class GatewaysItemComponent {
|
|||||||
private readonly api = inject(ApiService)
|
private readonly api = inject(ApiService)
|
||||||
private readonly formDialog = inject(FormDialogService)
|
private readonly formDialog = inject(FormDialogService)
|
||||||
|
|
||||||
readonly proxy = input.required<GatewayWithID>()
|
readonly proxy = input.required<GatewayPlus>()
|
||||||
|
|
||||||
open = false
|
open = false
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { i18nPipe } from '@start9labs/shared'
|
|||||||
import { TuiSkeleton } from '@taiga-ui/kit'
|
import { TuiSkeleton } from '@taiga-ui/kit'
|
||||||
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 { TableComponent } from 'src/app/routes/portal/components/table.component'
|
||||||
import { GatewaysItemComponent, GatewayWithID } from './item.component'
|
import { GatewaysItemComponent, GatewayPlus } from './item.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: '[gateways]',
|
selector: '[gateways]',
|
||||||
@@ -13,7 +13,7 @@ import { GatewaysItemComponent, GatewayWithID } from './item.component'
|
|||||||
'Name',
|
'Name',
|
||||||
'Type',
|
'Type',
|
||||||
'Access',
|
'Access',
|
||||||
$any('LAN IPs'),
|
$any('LAN IP'),
|
||||||
$any('WAN IP'),
|
$any('WAN IP'),
|
||||||
null,
|
null,
|
||||||
]"
|
]"
|
||||||
@@ -25,7 +25,7 @@ import { GatewaysItemComponent, GatewayWithID } from './item.component'
|
|||||||
<td colspan="5">
|
<td colspan="5">
|
||||||
@if (gateways()) {
|
@if (gateways()) {
|
||||||
<app-placeholder icon="@tui.door-closed-locked">
|
<app-placeholder icon="@tui.door-closed-locked">
|
||||||
<!-- @TODO Matt finalize text and add translations -->
|
<!-- @TODO translation -->
|
||||||
No gateways
|
No gateways
|
||||||
</app-placeholder>
|
</app-placeholder>
|
||||||
} @else {
|
} @else {
|
||||||
@@ -45,6 +45,6 @@ import { GatewaysItemComponent, GatewayWithID } from './item.component'
|
|||||||
PlaceholderComponent,
|
PlaceholderComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class GatewaysTableComponent<T extends GatewayWithID> {
|
export class GatewaysTableComponent<T extends GatewayPlus> {
|
||||||
readonly gateways = input<readonly T[] | null>(null)
|
readonly gateways = input<readonly T[] | null>(null)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ import { SystemWipeComponent } from './wipe.component'
|
|||||||
<div tuiCell tuiAppearance="outline-grayscale">
|
<div tuiCell tuiAppearance="outline-grayscale">
|
||||||
<tui-icon icon="@tui.app-window" />
|
<tui-icon icon="@tui.app-window" />
|
||||||
<span tuiTitle>
|
<span tuiTitle>
|
||||||
<strong>{{ 'Browser Tab Title' | i18n }}</strong>
|
<strong>{{ 'Browser tab title' | i18n }}</strong>
|
||||||
<span tuiSubtitle>
|
<span tuiSubtitle>
|
||||||
{{ 'Customize the name appearing in your browser tab' | i18n }}
|
{{ 'Customize the name appearing in your browser tab' | i18n }}
|
||||||
</span>
|
</span>
|
||||||
@@ -302,7 +302,7 @@ export default class SystemGeneralComponent {
|
|||||||
onTitle() {
|
onTitle() {
|
||||||
const sub = this.dialog
|
const sub = this.dialog
|
||||||
.openPrompt<string>({
|
.openPrompt<string>({
|
||||||
label: 'Browser Tab Title',
|
label: 'Browser tab title',
|
||||||
data: {
|
data: {
|
||||||
label: 'Device Name',
|
label: 'Device Name',
|
||||||
message:
|
message:
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export class WifiTableComponent {
|
|||||||
await this.component.saveAndConnect(network.ssid)
|
await this.component.saveAndConnect(network.ssid)
|
||||||
} else {
|
} else {
|
||||||
this.formDialog.open<FormContext<WiFiForm>>(FormComponent, {
|
this.formDialog.open<FormContext<WiFiForm>>(FormComponent, {
|
||||||
label: 'Password Needed',
|
label: 'Password needed',
|
||||||
data: {
|
data: {
|
||||||
spec: wifiSpec.spec,
|
spec: wifiSpec.spec,
|
||||||
buttons: [
|
buttons: [
|
||||||
|
|||||||
@@ -234,7 +234,28 @@ export namespace RR {
|
|||||||
}
|
}
|
||||||
export type CreateBackupRes = null
|
export type CreateBackupRes = null
|
||||||
|
|
||||||
// tunnel
|
// network
|
||||||
|
|
||||||
|
export type AddDomainReq = {
|
||||||
|
fqdn: string
|
||||||
|
gateway: string
|
||||||
|
acme: string | null
|
||||||
|
} // net.domain.add
|
||||||
|
export type AddDomainRes = null
|
||||||
|
|
||||||
|
export type RemoveDomainReq = {
|
||||||
|
fqdn: string
|
||||||
|
} // net.domain.remove
|
||||||
|
export type RemoveDomainRes = null
|
||||||
|
|
||||||
|
export type TestDomainReq = {
|
||||||
|
fqdn: string
|
||||||
|
gateway: string
|
||||||
|
} // net.domain.test
|
||||||
|
export type TestDomainRes = {
|
||||||
|
root: boolean
|
||||||
|
wildcard: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export type AddTunnelReq = {
|
export type AddTunnelReq = {
|
||||||
name: string
|
name: string
|
||||||
@@ -255,7 +276,7 @@ export namespace RR {
|
|||||||
export type RemoveTunnelRes = null
|
export type RemoveTunnelRes = null
|
||||||
|
|
||||||
export type InitAcmeReq = {
|
export type InitAcmeReq = {
|
||||||
provider: 'letsencrypt' | 'letsencrypt-staging' | string
|
provider: string
|
||||||
contact: string[]
|
contact: string[]
|
||||||
}
|
}
|
||||||
export type InitAcmeRes = null
|
export type InitAcmeRes = null
|
||||||
@@ -288,19 +309,19 @@ export namespace RR {
|
|||||||
export type ServerRemoveOnionReq = ServerAddOnionReq // server.host.address.onion.remove
|
export type ServerRemoveOnionReq = ServerAddOnionReq // server.host.address.onion.remove
|
||||||
export type RemoveOnionRes = null
|
export type RemoveOnionRes = null
|
||||||
|
|
||||||
export type ServerAddDomainReq = {
|
export type OsUiAddDomainReq = {
|
||||||
// server.host.address.domain.add
|
// server.host.address.domain.add
|
||||||
domain: string // FQDN
|
domain: string // FQDN
|
||||||
private: boolean
|
private: boolean
|
||||||
acme: string | null // "letsencrypt" | "letsencrypt-staging" | Url | null
|
acme: string | null // Url | null
|
||||||
}
|
}
|
||||||
export type AddDomainRes = null
|
export type OsUiAddDomainRes = null
|
||||||
|
|
||||||
export type ServerRemoveDomainReq = {
|
export type OsUiRemoveDomainReq = {
|
||||||
// server.host.address.domain.remove
|
// server.host.address.domain.remove
|
||||||
domain: string // FQDN
|
domain: string // FQDN
|
||||||
}
|
}
|
||||||
export type RemoveDomainRes = null
|
export type OsUiRemoveDomainRes = null
|
||||||
|
|
||||||
export type PkgBindingSetPublicReq = ServerBindingSetPublicReq & {
|
export type PkgBindingSetPublicReq = ServerBindingSetPublicReq & {
|
||||||
// package.host.binding.set-public
|
// package.host.binding.set-public
|
||||||
@@ -316,17 +337,19 @@ export namespace RR {
|
|||||||
|
|
||||||
export type PkgRemoveOnionReq = PkgAddOnionReq // package.host.address.onion.remove
|
export type PkgRemoveOnionReq = PkgAddOnionReq // package.host.address.onion.remove
|
||||||
|
|
||||||
export type PkgAddDomainReq = ServerAddDomainReq & {
|
export type PkgAddDomainReq = OsUiAddDomainReq & {
|
||||||
// package.host.address.domain.add
|
// package.host.address.domain.add
|
||||||
package: T.PackageId // string
|
package: T.PackageId // string
|
||||||
host: T.HostId // string
|
host: T.HostId // string
|
||||||
}
|
}
|
||||||
|
export type PkgAddDomainRes = null
|
||||||
|
|
||||||
export type PkgRemoveDomainReq = ServerRemoveDomainReq & {
|
export type PkgRemoveDomainReq = OsUiRemoveDomainReq & {
|
||||||
// package.host.address.domain.remove
|
// package.host.address.domain.remove
|
||||||
package: T.PackageId // string
|
package: T.PackageId // string
|
||||||
host: T.HostId // string
|
host: T.HostId // string
|
||||||
}
|
}
|
||||||
|
export type PkgRemoveDomainRes = null
|
||||||
|
|
||||||
export type GetPackageLogsReq = FetchLogsReq & { id: string } // package.logs
|
export type GetPackageLogsReq = FetchLogsReq & { id: string } // package.logs
|
||||||
export type GetPackageLogsRes = FetchLogsRes
|
export type GetPackageLogsRes = FetchLogsRes
|
||||||
@@ -624,32 +647,6 @@ export type DependencyErrorTransitive = {
|
|||||||
// @TODO 041
|
// @TODO 041
|
||||||
|
|
||||||
// export namespace RR041 {
|
// export namespace RR041 {
|
||||||
// // ** domains **
|
|
||||||
|
|
||||||
// export type ClaimStart9ToReq = { gatewayId: string } // net.domain.me.claim
|
|
||||||
// export type ClaimStart9ToRes = null
|
|
||||||
|
|
||||||
// export type DeleteStart9ToReq = {} // net.domain.me.delete
|
|
||||||
// export type DeleteStart9ToRes = null
|
|
||||||
|
|
||||||
// export type AddDomainReq = {
|
|
||||||
// hostname: string
|
|
||||||
// provider: {
|
|
||||||
// name: string
|
|
||||||
// username: string | null
|
|
||||||
// password: string | null
|
|
||||||
// }
|
|
||||||
// gatewayId: string
|
|
||||||
// } // net.domain.add
|
|
||||||
// export type AddDomainRes = null
|
|
||||||
|
|
||||||
// export type DeleteDomainReq = { hostname: string } // net.domain.delete
|
|
||||||
// export type DeleteDomainRes = null
|
|
||||||
|
|
||||||
// // port forwards
|
|
||||||
|
|
||||||
// export type OverridePortReq = { target: number; port: number } // net.port-forwards.override
|
|
||||||
// export type OverridePortRes = null
|
|
||||||
|
|
||||||
// // ** automated backups **
|
// // ** automated backups **
|
||||||
|
|
||||||
@@ -731,20 +728,6 @@ export type DependencyErrorTransitive = {
|
|||||||
|
|
||||||
// @TODO 041 types
|
// @TODO 041 types
|
||||||
|
|
||||||
// export type AppMetrics = {
|
|
||||||
// memory: {
|
|
||||||
// percentageUsed: MetricData
|
|
||||||
// used: MetricData
|
|
||||||
// }
|
|
||||||
// cpu: {
|
|
||||||
// percentageUsed: MetricData
|
|
||||||
// }
|
|
||||||
// disk: {
|
|
||||||
// percentageUsed: MetricData
|
|
||||||
// used: MetricData
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export type RemoteBackupTarget = CifsBackupTarget | CloudBackupTarget
|
// export type RemoteBackupTarget = CifsBackupTarget | CloudBackupTarget
|
||||||
// export type BackupTarget = RemoteBackupTarget | DiskBackupTarget
|
// export type BackupTarget = RemoteBackupTarget | DiskBackupTarget
|
||||||
|
|
||||||
|
|||||||
@@ -184,33 +184,15 @@ export abstract class ApiService {
|
|||||||
|
|
||||||
// @TODO 041
|
// @TODO 041
|
||||||
|
|
||||||
// abstract setOutboundProxy(
|
|
||||||
// params: RR.SetOutboundTunnelReq,
|
|
||||||
// ): Promise<RR.SetOutboundTunnelRes>
|
|
||||||
|
|
||||||
// ** domains **
|
// ** domains **
|
||||||
|
|
||||||
// @TODO 041
|
// @TODO 041
|
||||||
|
|
||||||
// abstract claimStart9ToDomain(
|
abstract addDomain(params: RR.AddDomainReq): Promise<RR.AddDomainRes>
|
||||||
// params: RR.ClaimStart9ToReq,
|
|
||||||
// ): Promise<RR.ClaimStart9ToRes>
|
|
||||||
|
|
||||||
// abstract deleteStart9ToDomain(
|
abstract removeDomain(params: RR.RemoveDomainReq): Promise<RR.RemoveDomainRes>
|
||||||
// params: RR.DeleteStart9ToReq,
|
|
||||||
// ): Promise<RR.DeleteStart9ToRes>
|
|
||||||
|
|
||||||
// abstract addDomain(params: RR.AddDomainReq): Promise<RR.AddDomainRes>
|
abstract testDomain(params: RR.TestDomainReq): Promise<RR.TestDomainRes>
|
||||||
|
|
||||||
// abstract deleteDomain(params: RR.DeleteDomainReq): Promise<RR.DeleteDomainRes>
|
|
||||||
|
|
||||||
// ** port forwards **
|
|
||||||
|
|
||||||
// @TODO 041
|
|
||||||
|
|
||||||
// abstract overridePortForward(
|
|
||||||
// params: RR.OverridePortReq,
|
|
||||||
// ): Promise<RR.OverridePortRes>
|
|
||||||
|
|
||||||
// wifi
|
// wifi
|
||||||
|
|
||||||
@@ -387,13 +369,13 @@ export abstract class ApiService {
|
|||||||
params: RR.ServerRemoveOnionReq,
|
params: RR.ServerRemoveOnionReq,
|
||||||
): Promise<RR.RemoveOnionRes>
|
): Promise<RR.RemoveOnionRes>
|
||||||
|
|
||||||
abstract serverAddDomain(
|
abstract osUiAddDomain(
|
||||||
params: RR.ServerAddDomainReq,
|
params: RR.OsUiAddDomainReq,
|
||||||
): Promise<RR.AddDomainRes>
|
): Promise<RR.OsUiAddDomainRes>
|
||||||
|
|
||||||
abstract serverRemoveDomain(
|
abstract osUiRemoveDomain(
|
||||||
params: RR.ServerRemoveDomainReq,
|
params: RR.OsUiRemoveDomainReq,
|
||||||
): Promise<RR.RemoveDomainRes>
|
): Promise<RR.OsUiRemoveDomainRes>
|
||||||
|
|
||||||
abstract pkgBindingSetPubic(
|
abstract pkgBindingSetPubic(
|
||||||
params: RR.PkgBindingSetPublicReq,
|
params: RR.PkgBindingSetPublicReq,
|
||||||
@@ -405,9 +387,9 @@ export abstract class ApiService {
|
|||||||
params: RR.PkgRemoveOnionReq,
|
params: RR.PkgRemoveOnionReq,
|
||||||
): Promise<RR.RemoveOnionRes>
|
): Promise<RR.RemoveOnionRes>
|
||||||
|
|
||||||
abstract pkgAddDomain(params: RR.PkgAddDomainReq): Promise<RR.AddDomainRes>
|
abstract pkgAddDomain(params: RR.PkgAddDomainReq): Promise<RR.PkgAddDomainRes>
|
||||||
|
|
||||||
abstract pkgRemoveDomain(
|
abstract pkgRemoveDomain(
|
||||||
params: RR.PkgRemoveDomainReq,
|
params: RR.PkgRemoveDomainReq,
|
||||||
): Promise<RR.RemoveDomainRes>
|
): Promise<RR.PkgRemoveDomainRes>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -358,41 +358,19 @@ export class LiveApiService extends ApiService {
|
|||||||
return this.rpcRequest({ method: 'net.tunnel.remove', params })
|
return this.rpcRequest({ method: 'net.tunnel.remove', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// async setOutboundProxy(
|
|
||||||
// params: RR.SetOutboundTunnelReq,
|
|
||||||
// ): Promise<RR.SetOutboundTunnelRes> {
|
|
||||||
// return this.rpcRequest({ method: 'server.proxy.set-outbound', params })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// domains
|
// domains
|
||||||
|
|
||||||
// async claimStart9ToDomain(
|
async addDomain(params: RR.AddDomainReq): Promise<RR.AddDomainRes> {
|
||||||
// params: RR.ClaimStart9ToReq,
|
return this.rpcRequest({ method: 'net.domain.add', params })
|
||||||
// ): Promise<RR.ClaimStart9ToRes> {
|
}
|
||||||
// return this.rpcRequest({ method: 'net.domain.me.claim', params })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async deleteStart9ToDomain(
|
async removeDomain(params: RR.RemoveDomainReq): Promise<RR.RemoveDomainRes> {
|
||||||
// params: RR.DeleteStart9ToReq,
|
return this.rpcRequest({ method: 'net.domain.remove', params })
|
||||||
// ): Promise<RR.DeleteStart9ToRes> {
|
}
|
||||||
// return this.rpcRequest({ method: 'net.domain.me.delete', params })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async addDomain(params: RR.AddDomainReq): Promise<RR.AddDomainRes> {
|
async testDomain(params: RR.TestDomainReq): Promise<RR.TestDomainRes> {
|
||||||
// return this.rpcRequest({ method: 'net.domain.add', params })
|
return this.rpcRequest({ method: 'net.domain.test', params })
|
||||||
// }
|
}
|
||||||
|
|
||||||
// async deleteDomain(params: RR.DeleteDomainReq): Promise<RR.DeleteDomainRes> {
|
|
||||||
// return this.rpcRequest({ method: 'net.domain.delete', params })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// port forwards
|
|
||||||
|
|
||||||
// async overridePortForward(
|
|
||||||
// params: RR.OverridePortReq,
|
|
||||||
// ): Promise<RR.OverridePortRes> {
|
|
||||||
// return this.rpcRequest({ method: 'net.port-forwards.override', params })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// wifi
|
// wifi
|
||||||
|
|
||||||
@@ -685,18 +663,18 @@ export class LiveApiService extends ApiService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async serverAddDomain(
|
async osUiAddDomain(
|
||||||
params: RR.ServerAddDomainReq,
|
params: RR.OsUiAddDomainReq,
|
||||||
): Promise<RR.AddDomainRes> {
|
): Promise<RR.OsUiAddDomainRes> {
|
||||||
return this.rpcRequest({
|
return this.rpcRequest({
|
||||||
method: 'server.host.address.domain.add',
|
method: 'server.host.address.domain.add',
|
||||||
params,
|
params,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async serverRemoveDomain(
|
async osUiRemoveDomain(
|
||||||
params: RR.ServerRemoveDomainReq,
|
params: RR.OsUiRemoveDomainReq,
|
||||||
): Promise<RR.RemoveDomainRes> {
|
): Promise<RR.OsUiRemoveDomainRes> {
|
||||||
return this.rpcRequest({
|
return this.rpcRequest({
|
||||||
method: 'server.host.address.domain.remove',
|
method: 'server.host.address.domain.remove',
|
||||||
params,
|
params,
|
||||||
@@ -728,7 +706,7 @@ export class LiveApiService extends ApiService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async pkgAddDomain(params: RR.PkgAddDomainReq): Promise<RR.AddDomainRes> {
|
async pkgAddDomain(params: RR.PkgAddDomainReq): Promise<RR.PkgAddDomainRes> {
|
||||||
return this.rpcRequest({
|
return this.rpcRequest({
|
||||||
method: 'package.host.address.domain.add',
|
method: 'package.host.address.domain.add',
|
||||||
params,
|
params,
|
||||||
@@ -737,7 +715,7 @@ export class LiveApiService extends ApiService {
|
|||||||
|
|
||||||
async pkgRemoveDomain(
|
async pkgRemoveDomain(
|
||||||
params: RR.PkgRemoveDomainReq,
|
params: RR.PkgRemoveDomainReq,
|
||||||
): Promise<RR.RemoveDomainRes> {
|
): Promise<RR.PkgRemoveDomainRes> {
|
||||||
return this.rpcRequest({
|
return this.rpcRequest({
|
||||||
method: 'package.host.address.domain.remove',
|
method: 'package.host.address.domain.remove',
|
||||||
params,
|
params,
|
||||||
|
|||||||
@@ -601,113 +601,50 @@ export class MockApiService extends ApiService {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// async setOutboundProxy(
|
|
||||||
// params: RR.SetOutboundTunnelReq,
|
|
||||||
// ): Promise<RR.SetOutboundTunnelRes> {
|
|
||||||
// await pauseFor(2000)
|
|
||||||
|
|
||||||
// const patch: ReplaceOperation<string | null>[] = [
|
|
||||||
// {
|
|
||||||
// op: PatchOp.REPLACE,
|
|
||||||
// path: '/serverInfo/network/outboundInterface',
|
|
||||||
// value: params.id,
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
// this.mockRevision(patch)
|
|
||||||
|
|
||||||
// return null
|
|
||||||
// }
|
|
||||||
|
|
||||||
// domains
|
// domains
|
||||||
|
|
||||||
// async claimStart9ToDomain(
|
async addDomain(params: RR.AddDomainReq): Promise<RR.AddDomainRes> {
|
||||||
// params: RR.ClaimStart9ToReq,
|
await pauseFor(2000)
|
||||||
// ): Promise<RR.ClaimStart9ToRes> {
|
|
||||||
// await pauseFor(2000)
|
|
||||||
|
|
||||||
// const patch = [
|
const patch = [
|
||||||
// {
|
{
|
||||||
// op: PatchOp.REPLACE,
|
op: PatchOp.REPLACE,
|
||||||
// path: '/serverInfo/network/start9To',
|
path: `/serverInfo/network/domains`,
|
||||||
// value: {
|
value: {
|
||||||
// subdomain: 'xyz',
|
[params.fqdn]: {
|
||||||
// gatewayId: params.gatewayId,
|
gateway: params.gateway,
|
||||||
// },
|
acme: params.acme,
|
||||||
// },
|
},
|
||||||
// ]
|
},
|
||||||
// this.mockRevision(patch)
|
},
|
||||||
|
]
|
||||||
|
this.mockRevision(patch)
|
||||||
|
|
||||||
// return null
|
return null
|
||||||
// }
|
}
|
||||||
|
|
||||||
// async deleteStart9ToDomain(
|
async removeDomain(params: RR.RemoveDomainReq): Promise<RR.RemoveDomainRes> {
|
||||||
// params: RR.DeleteStart9ToReq,
|
await pauseFor(2000)
|
||||||
// ): Promise<RR.DeleteStart9ToRes> {
|
const patch = [
|
||||||
// await pauseFor(2000)
|
{
|
||||||
// const patch = [
|
op: PatchOp.REPLACE,
|
||||||
// {
|
path: '/serverInfo/network/domains',
|
||||||
// op: PatchOp.REPLACE,
|
value: {},
|
||||||
// path: '/serverInfo/network/start9To',
|
},
|
||||||
// value: null,
|
]
|
||||||
// },
|
this.mockRevision(patch)
|
||||||
// ]
|
|
||||||
// this.mockRevision(patch)
|
|
||||||
|
|
||||||
// return null
|
return null
|
||||||
// }
|
}
|
||||||
|
|
||||||
// async addDomain(params: RR.AddDomainReq): Promise<RR.AddDomainRes> {
|
async testDomain(params: RR.TestDomainReq): Promise<RR.TestDomainRes> {
|
||||||
// await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
|
|
||||||
// const patch = [
|
return {
|
||||||
// {
|
root: true,
|
||||||
// op: PatchOp.REPLACE,
|
wildcard: true,
|
||||||
// path: `/serverInfo/network/domains`,
|
}
|
||||||
// value: {
|
}
|
||||||
// [params.hostname]: {
|
|
||||||
// gatewayId: params.gatewayId,
|
|
||||||
// provider: params.provider.name,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
// this.mockRevision(patch)
|
|
||||||
|
|
||||||
// return null
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async deleteDomain(params: RR.DeleteDomainReq): Promise<RR.DeleteDomainRes> {
|
|
||||||
// await pauseFor(2000)
|
|
||||||
// const patch = [
|
|
||||||
// {
|
|
||||||
// op: PatchOp.REPLACE,
|
|
||||||
// path: '/serverInfo/network/domains',
|
|
||||||
// value: {},
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
// this.mockRevision(patch)
|
|
||||||
|
|
||||||
// return null
|
|
||||||
// }
|
|
||||||
|
|
||||||
// port forwards
|
|
||||||
|
|
||||||
// async overridePortForward(
|
|
||||||
// params: RR.OverridePortReq,
|
|
||||||
// ): Promise<RR.OverridePortRes> {
|
|
||||||
// await pauseFor(2000)
|
|
||||||
|
|
||||||
// const patch = [
|
|
||||||
// {
|
|
||||||
// op: PatchOp.REPLACE,
|
|
||||||
// path: '/serverInfo/network/wanConfig/forwards/0/override',
|
|
||||||
// value: params.port,
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
// this.mockRevision(patch)
|
|
||||||
|
|
||||||
// return null
|
|
||||||
// }
|
|
||||||
|
|
||||||
// wifi
|
// wifi
|
||||||
|
|
||||||
@@ -1496,7 +1433,9 @@ export class MockApiService extends ApiService {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async serverAddDomain(params: RR.PkgAddDomainReq): Promise<RR.AddDomainRes> {
|
async osUiAddDomain(
|
||||||
|
params: RR.OsUiAddDomainReq,
|
||||||
|
): Promise<RR.OsUiAddDomainRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
|
|
||||||
const patch: Operation<any>[] = [
|
const patch: Operation<any>[] = [
|
||||||
@@ -1529,9 +1468,9 @@ export class MockApiService extends ApiService {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async serverRemoveDomain(
|
async osUiRemoveDomain(
|
||||||
params: RR.PkgRemoveDomainReq,
|
params: RR.OsUiRemoveDomainReq,
|
||||||
): Promise<RR.RemoveDomainRes> {
|
): Promise<RR.OsUiRemoveDomainRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
|
|
||||||
const patch: RemoveOperation[] = [
|
const patch: RemoveOperation[] = [
|
||||||
@@ -1613,7 +1552,7 @@ export class MockApiService extends ApiService {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async pkgAddDomain(params: RR.PkgAddDomainReq): Promise<RR.AddDomainRes> {
|
async pkgAddDomain(params: RR.PkgAddDomainReq): Promise<RR.PkgAddDomainRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
|
|
||||||
const patch: Operation<any>[] = [
|
const patch: Operation<any>[] = [
|
||||||
@@ -1648,7 +1587,7 @@ export class MockApiService extends ApiService {
|
|||||||
|
|
||||||
async pkgRemoveDomain(
|
async pkgRemoveDomain(
|
||||||
params: RR.PkgRemoveDomainReq,
|
params: RR.PkgRemoveDomainReq,
|
||||||
): Promise<RR.RemoveDomainRes> {
|
): Promise<RR.PkgRemoveDomainRes> {
|
||||||
await pauseFor(2000)
|
await pauseFor(2000)
|
||||||
|
|
||||||
const patch: RemoveOperation[] = [
|
const patch: RemoveOperation[] = [
|
||||||
|
|||||||
@@ -32,6 +32,16 @@ export const mockPatchData: DataModel = {
|
|||||||
contact: ['mailto:support@start9.com'],
|
contact: ['mailto:support@start9.com'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
domains: {
|
||||||
|
'cloud.private.com': {
|
||||||
|
gateway: 'eth0',
|
||||||
|
acme: null,
|
||||||
|
},
|
||||||
|
'public.com': {
|
||||||
|
gateway: 'wireguard1',
|
||||||
|
acme: 'https://acme-v02.api.letsencrypt.org/directory',
|
||||||
|
},
|
||||||
|
},
|
||||||
host: {
|
host: {
|
||||||
bindings: {
|
bindings: {
|
||||||
80: {
|
80: {
|
||||||
|
|||||||
@@ -1,7 +1,20 @@
|
|||||||
import { Languages } from '@start9labs/shared'
|
import { Languages } from '@start9labs/shared'
|
||||||
import { T } from '@start9labs/start-sdk'
|
import { T } from '@start9labs/start-sdk'
|
||||||
|
|
||||||
export type DataModel = T.Public & { ui: UIData; packageData: AllPackageData }
|
export type DataModel = T.Public & {
|
||||||
|
ui: UIData
|
||||||
|
packageData: AllPackageData
|
||||||
|
serverInfo: T.ServerInfo & {
|
||||||
|
network: T.NetworkInfo & {
|
||||||
|
domains: {
|
||||||
|
[fqdn: string]: {
|
||||||
|
gateway: string
|
||||||
|
acme: string | null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type UIData = {
|
export type UIData = {
|
||||||
name: string | null
|
name: string | null
|
||||||
@@ -11,22 +24,6 @@ export type UIData = {
|
|||||||
language: Languages
|
language: Languages
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NetworkInfo = T.NetworkInfo & {
|
|
||||||
// @TODO 041
|
|
||||||
// start9To: {
|
|
||||||
// subdomain: string
|
|
||||||
// gatewayId: string
|
|
||||||
// } | null
|
|
||||||
// domains: {
|
|
||||||
// [key: string]: Domain
|
|
||||||
// }
|
|
||||||
// wanConfig: {
|
|
||||||
// upnp: boolean
|
|
||||||
// forwards: PortForward[]
|
|
||||||
// }
|
|
||||||
// outboundProxy: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PackageDataEntry<T extends StateInfo = StateInfo> =
|
export type PackageDataEntry<T extends StateInfo = StateInfo> =
|
||||||
T.PackageDataEntry & {
|
T.PackageDataEntry & {
|
||||||
stateInfo: T
|
stateInfo: T
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
// const options: Partial<
|
// const options: Partial<
|
||||||
// TuiDialogOptions<FormContext<typeof config.validator._TYPE>>
|
// TuiDialogOptions<FormContext<typeof config.validator._TYPE>>
|
||||||
// > = {
|
// > = {
|
||||||
// label: 'Outbound Proxy',
|
// label: 'Outbound proxy',
|
||||||
// data: {
|
// data: {
|
||||||
// spec: await configBuilderToSpec(config),
|
// spec: await configBuilderToSpec(config),
|
||||||
// buttons: [
|
// buttons: [
|
||||||
|
|||||||
Reference in New Issue
Block a user