mirror of
https://github.com/google/nomulus
synced 2026-07-07 08:36:50 +00:00
Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dfef733360 | |||
| 04a0659197 | |||
| 70010886b1 | |||
| 3cd50dc929 | |||
| 03872b508f | |||
| 1096f201cd | |||
| 9dc3215624 | |||
| af321fb65e | |||
| c5132c04be | |||
| a64dc21f96 | |||
| 0381533a35 | |||
| 4999a72d96 | |||
| 2d072c3844 | |||
| c15dec4419 | |||
| 8340125bf4 | |||
| 98ba80d94e | |||
| 967d04efce | |||
| 20fd944e83 | |||
| daa56e6d85 | |||
| ed33c7424d | |||
| 04b30f5c04 | |||
| 11702bc940 | |||
| 2d82646421 | |||
| 50260dca5f | |||
| 3cc10bfe0d | |||
| 5645b2e218 | |||
| 2a01c12b14 | |||
| 93d77e558f | |||
| 92ebd0dedb | |||
| b49e37feee | |||
| bede56598c | |||
| 467d9c7bf1 | |||
| e5ebe96c74 | |||
| 2ff4d97b0a | |||
| 6b0beeb477 | |||
| d2d43f4115 | |||
| 12fd206c35 | |||
| a3f510d0db | |||
| fa54c26ee2 | |||
| 8896fb94f4 | |||
| 6c7bf5e5dd | |||
| ea1e8d5cc5 | |||
| 7fb846c5b0 | |||
| 5180095cb6 | |||
| 9fe64bf9ec | |||
| 0f3b62d5ce | |||
| bd4701647b | |||
| fb816d7a2c | |||
| 8fbf363195 | |||
| 397f800614 | |||
| bcf42bd287 | |||
| ed95d19b93 | |||
| 97fc2c0b66 | |||
| 00728c40ba | |||
| 3f2a42ab8d | |||
| b73e342820 | |||
| df7fec7a3e | |||
| 6f7ae1eabc | |||
| eb978ebbd5 | |||
| 95831bc8b7 | |||
| 538260521b | |||
| 612708f0a8 | |||
| e78de98060 | |||
| c918258fb1 | |||
| 34103ec815 | |||
| a63812160e | |||
| 9aaf7ee36a | |||
| 96a864dbd6 | |||
| 8a36fb5f1f | |||
| 6c138420b0 | |||
| 08570511f5 | |||
| e62d970d34 | |||
| 067927b735 | |||
| 4ec2919ce3 | |||
| 19422075fa | |||
| 40b6984ffb | |||
| 6952e0f653 | |||
| dcb55d27bb | |||
| 765bd9834a | |||
| 221088e738 | |||
| 6649e00df7 | |||
| 2ceb52a7c4 | |||
| 120bcc33be | |||
| 8987fd37c2 | |||
| 653e092ad4 | |||
| 5e97a8b412 | |||
| 229fcf3946 | |||
| b775e4a178 | |||
| e3c386a8a7 | |||
| 799f0449ad | |||
| bf025445d5 | |||
| 9f22f2e8ae | |||
| 45c8b81823 | |||
| 4cfcc60655 | |||
| e4ee63b8f3 | |||
| f8407c74bc | |||
| 693467a165 |
@@ -12,16 +12,16 @@ Nomulus is an open source, scalable, cloud-based service for operating
|
|||||||
[top-level domains](https://en.wikipedia.org/wiki/Top-level_domain) (TLDs). It
|
[top-level domains](https://en.wikipedia.org/wiki/Top-level_domain) (TLDs). It
|
||||||
is the authoritative source for the TLDs that it runs, meaning that it is
|
is the authoritative source for the TLDs that it runs, meaning that it is
|
||||||
responsible for tracking domain name ownership and handling registrations,
|
responsible for tracking domain name ownership and handling registrations,
|
||||||
renewals, availability checks, and WHOIS requests. End-user registrants (i.e.
|
renewals, availability checks, and WHOIS requests. End-user registrants (i.e.,
|
||||||
people or companies that want to register a domain name) use an intermediate
|
people or companies that want to register a domain name) use an intermediate
|
||||||
domain name registrar acting on their behalf to interact with the registry.
|
domain name registrar acting on their behalf to interact with the registry.
|
||||||
|
|
||||||
Nomulus runs on [Google App Engine][gae] and is written primarily in Java. It is
|
Nomulus runs on [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine)
|
||||||
the software that [Google Registry](https://www.registry.google/) uses to
|
and is written primarily in Java. It is the software that
|
||||||
operate TLDs such as .google, .app, .how, .soy, and .みんな. It can run any
|
[Google Registry](https://www.registry.google/) uses to operate TLDs such as .google,
|
||||||
number of TLDs in a single shared registry system using horizontal scaling. Its
|
.app, .how, .soy, and .みんな. It can run any number of TLDs in a single shared registry
|
||||||
source code is publicly available in this repository under the [Apache 2.0 free
|
system using horizontal scaling. Its source code is publicly available in this
|
||||||
and open source license](https://www.apache.org/licenses/LICENSE-2.0).
|
repository under the [Apache 2.0 free and open source license](https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
@@ -30,8 +30,8 @@ running system:
|
|||||||
|
|
||||||
* [Install
|
* [Install
|
||||||
guide](https://github.com/google/nomulus/blob/master/docs/install.md)
|
guide](https://github.com/google/nomulus/blob/master/docs/install.md)
|
||||||
* View the source code for the [GAE app](https://github.com/google/nomulus/tree/master/core/src/main/java/google/registry)
|
* View the source code for the [Main HTTP server](https://github.com/google/nomulus/tree/master/core/src/main/java/google/registry)
|
||||||
and for the [GKE proxy](https://github.com/google/nomulus/tree/master/proxy/src/main/java/google/registry)
|
and for the [EPP proxy](https://github.com/google/nomulus/tree/master/proxy/src/main/java/google/registry)
|
||||||
* [Other docs](https://github.com/google/nomulus/tree/master/docs)
|
* [Other docs](https://github.com/google/nomulus/tree/master/docs)
|
||||||
* [Javadoc](https://javadoc.nomulus.foo/)
|
* [Javadoc](https://javadoc.nomulus.foo/)
|
||||||
* [Nomulus discussion
|
* [Nomulus discussion
|
||||||
@@ -54,9 +54,11 @@ Nomulus has the following capabilities:
|
|||||||
checking, updating, and transferring domain names.
|
checking, updating, and transferring domain names.
|
||||||
* **[DNS](https://en.wikipedia.org/wiki/Domain_Name_System) interface**: The
|
* **[DNS](https://en.wikipedia.org/wiki/Domain_Name_System) interface**: The
|
||||||
registry provides a pluggable interface that can be implemented to handle
|
registry provides a pluggable interface that can be implemented to handle
|
||||||
different DNS providers. It includes a sample implementation using Google
|
different DNS providers. It includes a sample implementation using [Google
|
||||||
Cloud DNS as well as an RFC 2136 compliant implementation that works with
|
Cloud DNS](https://cloud.google.com/dns/), as well as an RFC 2136 compliant
|
||||||
BIND.
|
implementation that works with BIND. If you are using Google Cloud DNS, you
|
||||||
|
may need to understand its capabilities and provide your own
|
||||||
|
multi-[AS](https://en.wikipedia.org/wiki/Autonomous_system_\(Internet\)) solution.
|
||||||
* **[WHOIS](https://en.wikipedia.org/wiki/WHOIS)**: A text-based protocol that
|
* **[WHOIS](https://en.wikipedia.org/wiki/WHOIS)**: A text-based protocol that
|
||||||
returns ownership and contact information on registered domain names.
|
returns ownership and contact information on registered domain names.
|
||||||
* **[Registration Data Access Protocol
|
* **[Registration Data Access Protocol
|
||||||
@@ -68,7 +70,7 @@ Nomulus has the following capabilities:
|
|||||||
provider to allow take-over by another registry operator in the event of
|
provider to allow take-over by another registry operator in the event of
|
||||||
serious failure. This is required by ICANN for all [new
|
serious failure. This is required by ICANN for all [new
|
||||||
gTLDs](https://newgtlds.icann.org/).
|
gTLDs](https://newgtlds.icann.org/).
|
||||||
* **Premium pricing**: Communicates prices for premium domain names (i.e.
|
* **Premium pricing**: Communicates prices for premium domain names (i.e.,
|
||||||
those that are highly desirable) and supports configurable premium
|
those that are highly desirable) and supports configurable premium
|
||||||
registration and renewal prices. An extensible interface allows fully
|
registration and renewal prices. An extensible interface allows fully
|
||||||
programmatic pricing.
|
programmatic pricing.
|
||||||
@@ -91,56 +93,50 @@ Nomulus has the following capabilities:
|
|||||||
* **Administrative tool**: Performs the full range of administrative tasks
|
* **Administrative tool**: Performs the full range of administrative tasks
|
||||||
needed to manage a running registry system, including creating and
|
needed to manage a running registry system, including creating and
|
||||||
configuring new TLDs.
|
configuring new TLDs.
|
||||||
* **DNS interface**: An interface for DNS operations is provided so you can
|
* **Secure storage of cryptographic keys**: A keyring interface is
|
||||||
write an implementation for your chosen provider, along with a sample
|
provided for plugging in your own implementation (see [configuration
|
||||||
implementation that uses [Google Cloud DNS](https://cloud.google.com/dns/).
|
doc](https://github.com/google/nomulus/blob/master/docs/configuration.md)
|
||||||
If you are using Google Cloud DNS you may need to understand its
|
for details), and an implementation based on
|
||||||
capabilities and provide your own
|
[Google Cloud Secret Manager](https://cloud.google.com/security/products/secret-manager) is
|
||||||
multi-[AS](https://en.wikipedia.org/wiki/Autonomous_system_\(Internet\))
|
available.
|
||||||
solution.
|
* **TPC Proxy**: Nomulus is built on top of the [Jetty](https://jetty.org/)
|
||||||
* **GAE Proxy**: App Engine Standard only serves HTTP/S traffic. A proxy to
|
container that implements the [Jakarta Servlet](https://jakarta.ee/specifications/servlet/)
|
||||||
forward traffic on EPP and WHOIS ports to App Engine via HTTPS is provided.
|
specification and only serves HTTP/S traffic. A proxy to translate raw TCP traffic (e.g., EPP)
|
||||||
Instructions on setting up the proxy on
|
to and from HTTP is provided.
|
||||||
[Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/)
|
Instructions on setting up the proxy
|
||||||
is [available](https://github.com/google/nomulus/blob/master/docs/proxy-setup.md).
|
are [available](https://github.com/google/nomulus/blob/master/docs/proxy-setup.md).
|
||||||
Running the proxy on GKE supports IPv4 and IPv6 access, per ICANN's
|
The proxy can either run in a separate cluster and communicate to Nomulus public HTTP
|
||||||
requirements for gTLDs. The proxy can also run as a single jar file, or on
|
endpoints via the Internet, or as a sidecar with the Nomulus image in the same pod and
|
||||||
other Kubernetes providers, with modifications.
|
communicate to it via loopback.
|
||||||
|
|
||||||
## Additional components
|
## Additional components
|
||||||
|
|
||||||
Registry operators interested in deploying Nomulus will likely require some
|
Registry operators interested in deploying Nomulus will likely require some
|
||||||
additional components that are need to be configured separately.
|
additional components that need to be configured separately.
|
||||||
|
|
||||||
* A way to invoice registrars for domain name registrations and accept
|
* A way to invoice registrars for domain name registrations and accept
|
||||||
payments. Nomulus records the information required to generate invoices in
|
payments. Nomulus records the information required to generate invoices in
|
||||||
[billing
|
[billing
|
||||||
events](https://github.com/google/nomulus/blob/master/docs/code-structure.md#billing-events).
|
events](https://github.com/google/nomulus/blob/master/docs/code-structure.md#billing-events).
|
||||||
* Fully automated reporting to meet ICANN's requirements for gTLDs. Nomulus
|
* Fully automated reporting to meet ICANN's requirements for gTLDs. Nomulus
|
||||||
includes substantial reporting functionality but some additional work will
|
includes substantial reporting functionality, but some additional work will
|
||||||
be required by the operator in this area.
|
be required by the operator in this area.
|
||||||
* A secure method for storing cryptographic keys. A keyring interface is
|
|
||||||
provided for plugging in your own implementation (see [configuration
|
|
||||||
doc](https://github.com/google/nomulus/blob/master/docs/configuration.md)
|
|
||||||
for details).
|
|
||||||
* System status and uptime monitoring.
|
* System status and uptime monitoring.
|
||||||
|
|
||||||
## Outside references
|
## Outside references
|
||||||
|
|
||||||
* [Donuts](http://donuts.domains) Registry has helped review the code and
|
* [Identity Digital](http://identity.digital) has helped review the code and
|
||||||
provided valuable feedback
|
provided valuable feedback.
|
||||||
* [CoCCa](http://cocca.org.nz) and [FRED](https://fred.nic.cz) are other
|
* [CoCCa](http://cocca.org.nz) and [FRED](https://fred.nic.cz) are other
|
||||||
open-source registry platforms in use by many TLDs
|
open-source registry platforms in use by many TLDs.
|
||||||
* We are not aware of any fully open source domain registrar projects, but
|
* We are not aware of any fully open source domain registrar projects, but
|
||||||
open source EPP Toolkits (not yet tested with Nomulus; may require
|
open source EPP Toolkits (not yet tested with Nomulus; may require
|
||||||
integration work) include:
|
integration work) include:
|
||||||
* [EPP RTK Project](http://epp-rtk.sourceforge.net/)
|
* [Universal Registry/Registrar Toolkit](https://sourceforge.net/projects/epp-rtk/)
|
||||||
* [CentralNic](https://www.centralnic.com/registry/labs)
|
|
||||||
* [ari-toolkit](https://github.com/AusRegistry/ari-toolkit)
|
* [ari-toolkit](https://github.com/AusRegistry/ari-toolkit)
|
||||||
* [Net::DRI](https://metacpan.org/pod/Net::DRI)
|
* [Net::DRI](https://metacpan.org/pod/Net::DRI)
|
||||||
* Some Open Source DNS Projects that may be useful, but which we have not
|
* Some Open Source DNS Projects that may be useful, but which we have not
|
||||||
tested:
|
tested:
|
||||||
* [AtomiaDNS](http://atomiadns.com/)
|
* [AtomiaDNS](https://github.com/atomia/atomiadns)
|
||||||
* [PowerDNS](https://doc.powerdns.com/md/)
|
* [PowerDNS](https://github.com/PowerDNS/pdns)
|
||||||
|
|
||||||
[gae]:https://cloud.google.com/appengine/docs/about-the-standard-environment
|
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ explodeWar.doLast {
|
|||||||
|
|
||||||
appengineDeployAll.mustRunAfter ':console-webapp:deploy'
|
appengineDeployAll.mustRunAfter ':console-webapp:deploy'
|
||||||
appengineDeployAll.finalizedBy ':deployCloudSchedulerAndQueue'
|
appengineDeployAll.finalizedBy ':deployCloudSchedulerAndQueue'
|
||||||
rootProject.deploy.dependsOn appengineDeployAll
|
|
||||||
|
|
||||||
rootProject.stage.dependsOn appengineStage
|
rootProject.stage.dependsOn appengineStage
|
||||||
tasks['war'].dependsOn ':core:processResources'
|
tasks['war'].dependsOn ':core:processResources'
|
||||||
|
|||||||
+5
-9
@@ -95,26 +95,22 @@ task stage {
|
|||||||
description = 'Generates application directories for all services.'
|
description = 'Generates application directories for all services.'
|
||||||
}
|
}
|
||||||
|
|
||||||
// App-engine environment configuration. We set up all of the variables in
|
|
||||||
// the root project.
|
|
||||||
|
|
||||||
def environments = ['production', 'sandbox', 'alpha', 'crash', 'qa']
|
|
||||||
|
|
||||||
def gcpProject = null
|
def gcpProject = null
|
||||||
|
|
||||||
apply from: "${rootDir.path}/projects.gradle"
|
apply from: "${rootDir.path}/projects.gradle"
|
||||||
|
|
||||||
if (environment == '') {
|
if (environment == '') {
|
||||||
// Keep the project null, this will prevent deployment. Set the
|
// Keep the project null, this will prevent deployment. Set the
|
||||||
// environment to "alpha" because other code needs this property to
|
// environment to "alpha" because other code needs this property to
|
||||||
// explode the war file.
|
// explode the war file.
|
||||||
environment = 'alpha'
|
environment = 'alpha'
|
||||||
} else if (environment != 'production' && environment != 'sandbox') {
|
} else {
|
||||||
gcpProject = projects[environment]
|
gcpProject = projects[environment]
|
||||||
if (gcpProject == null) {
|
if (gcpProject == null) {
|
||||||
throw new GradleException("-Penvironment must be one of " +
|
throw new GradleException("-Penvironment must be one of " +
|
||||||
"${projects.keySet()}.")
|
"${projects.keySet()}.")
|
||||||
}
|
}
|
||||||
|
project(':console-webapp').setProperty('configuration', environment)
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.ext.environment = environment
|
rootProject.ext.environment = environment
|
||||||
@@ -561,7 +557,7 @@ task deployCloudSchedulerAndQueue {
|
|||||||
commandLine 'go', 'run',
|
commandLine 'go', 'run',
|
||||||
"./deployCloudSchedulerAndQueue.go",
|
"./deployCloudSchedulerAndQueue.go",
|
||||||
"${rootDir}/core/src/main/java/google/registry/config/files/nomulus-config-${env}.yaml",
|
"${rootDir}/core/src/main/java/google/registry/config/files/nomulus-config-${env}.yaml",
|
||||||
"${rootDir}/core/src/main/java/google/registry/env/${env}/default/WEB-INF/cloud-scheduler-tasks.xml",
|
"${rootDir}/core/src/main/java/google/registry/config/files/tasks/cloud-scheduler-tasks-${env}.xml",
|
||||||
"domain-registry-${env}"
|
"domain-registry-${env}"
|
||||||
}
|
}
|
||||||
exec {
|
exec {
|
||||||
@@ -569,7 +565,7 @@ task deployCloudSchedulerAndQueue {
|
|||||||
commandLine 'go', 'run',
|
commandLine 'go', 'run',
|
||||||
"./deployCloudSchedulerAndQueue.go",
|
"./deployCloudSchedulerAndQueue.go",
|
||||||
"${rootDir}/core/src/main/java/google/registry/config/files/nomulus-config-${env}.yaml",
|
"${rootDir}/core/src/main/java/google/registry/config/files/nomulus-config-${env}.yaml",
|
||||||
"${rootDir}/core/src/main/java/google/registry/env/common/default/WEB-INF/cloud-tasks-queue.xml",
|
"${rootDir}/core/src/main/java/google/registry/config/files/cloud-tasks-queue.xml",
|
||||||
"domain-registry-${env}"
|
"domain-registry-${env}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,4 +65,5 @@ dependencies {
|
|||||||
|
|
||||||
testImplementation deps['org.junit.jupiter:junit-jupiter-api']
|
testImplementation deps['org.junit.jupiter:junit-jupiter-api']
|
||||||
testImplementation deps['org.junit.jupiter:junit-jupiter-engine']
|
testImplementation deps['org.junit.jupiter:junit-jupiter-engine']
|
||||||
|
testImplementation deps['org.junit.platform:junit-platform-launcher']
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-10
@@ -3,7 +3,7 @@
|
|||||||
# This file is expected to be part of source control.
|
# This file is expected to be part of source control.
|
||||||
aopalliance:aopalliance:1.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
aopalliance:aopalliance:1.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||||
com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||||
com.github.ben-manes.caffeine:caffeine:3.1.8=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
com.github.ben-manes.caffeine:caffeine:3.2.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||||
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||||
com.google.auto.service:auto-service-annotations:1.0.1=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
com.google.auto.service:auto-service-annotations:1.0.1=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||||
com.google.auto.value:auto-value-annotations:1.11.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
com.google.auto.value:auto-value-annotations:1.11.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||||
@@ -12,7 +12,7 @@ com.google.auto:auto-common:1.2.1=annotationProcessor,errorprone,testAnnotationP
|
|||||||
com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,errorprone,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath,testing,testingAnnotationProcessor,testingCompileClasspath
|
com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,errorprone,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath,testing,testingAnnotationProcessor,testingCompileClasspath
|
||||||
com.google.errorprone:error_prone_annotation:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
com.google.errorprone:error_prone_annotation:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||||
com.google.errorprone:error_prone_annotations:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
com.google.errorprone:error_prone_annotations:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||||
com.google.errorprone:error_prone_annotations:2.28.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
com.google.errorprone:error_prone_annotations:2.36.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||||
com.google.errorprone:error_prone_annotations:2.7.1=checkstyle
|
com.google.errorprone:error_prone_annotations:2.7.1=checkstyle
|
||||||
com.google.errorprone:error_prone_check_api:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
com.google.errorprone:error_prone_check_api:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||||
com.google.errorprone:error_prone_core:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
com.google.errorprone:error_prone_core:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||||
@@ -38,9 +38,9 @@ info.picocli:picocli:4.6.2=checkstyle
|
|||||||
io.github.eisop:dataflow-errorprone:3.34.0-eisop1=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
io.github.eisop:dataflow-errorprone:3.34.0-eisop1=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||||
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||||
io.github.java-diff-utils:java-diff-utils:4.15=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
io.github.java-diff-utils:java-diff-utils:4.15=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||||
jakarta.inject:jakarta.inject-api:1.0.5=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
jakarta.inject:jakarta.inject-api:2.0.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||||
javax.inject:javax.inject:1=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
javax.inject:javax.inject:1=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||||
joda-time:joda-time:2.13.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
joda-time:joda-time:2.13.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||||
junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||||
net.sf.saxon:Saxon-HE:10.6=checkstyle
|
net.sf.saxon:Saxon-HE:10.6=checkstyle
|
||||||
org.antlr:antlr4-runtime:4.9.3=checkstyle
|
org.antlr:antlr4-runtime:4.9.3=checkstyle
|
||||||
@@ -55,12 +55,13 @@ org.jacoco:org.jacoco.ant:0.8.12=jacocoAnt
|
|||||||
org.jacoco:org.jacoco.core:0.8.12=jacocoAnt
|
org.jacoco:org.jacoco.core:0.8.12=jacocoAnt
|
||||||
org.jacoco:org.jacoco.report:0.8.12=jacocoAnt
|
org.jacoco:org.jacoco.report:0.8.12=jacocoAnt
|
||||||
org.javassist:javassist:3.28.0-GA=checkstyle
|
org.javassist:javassist:3.28.0-GA=checkstyle
|
||||||
org.jspecify:jspecify:0.3.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
org.jspecify:jspecify:1.0.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||||
org.junit.jupiter:junit-jupiter-api:5.11.4=testCompileClasspath,testRuntimeClasspath
|
org.junit.jupiter:junit-jupiter-api:5.12.1=testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.jupiter:junit-jupiter-engine:5.11.4=testCompileClasspath,testRuntimeClasspath
|
org.junit.jupiter:junit-jupiter-engine:5.12.1=testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.platform:junit-platform-commons:1.11.4=testCompileClasspath,testRuntimeClasspath
|
org.junit.platform:junit-platform-commons:1.12.1=testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.platform:junit-platform-engine:1.11.4=testCompileClasspath,testRuntimeClasspath
|
org.junit.platform:junit-platform-engine:1.12.1=testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit:junit-bom:5.11.4=testCompileClasspath,testRuntimeClasspath
|
org.junit.platform:junit-platform-launcher:1.12.1=testCompileClasspath,testRuntimeClasspath
|
||||||
|
org.junit:junit-bom:5.12.1=testCompileClasspath,testRuntimeClasspath
|
||||||
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
|
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
|
||||||
org.ow2.asm:asm-commons:9.7=jacocoAnt
|
org.ow2.asm:asm-commons:9.7=jacocoAnt
|
||||||
org.ow2.asm:asm-tree:9.7=jacocoAnt
|
org.ow2.asm:asm-tree:9.7=jacocoAnt
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ package google.registry.util;
|
|||||||
|
|
||||||
import static org.joda.time.DateTimeZone.UTC;
|
import static org.joda.time.DateTimeZone.UTC;
|
||||||
|
|
||||||
|
import jakarta.inject.Inject;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
/** Clock implementation that proxies to the real system clock. */
|
/** Clock implementation that proxies to the real system clock. */
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ package google.registry.util;
|
|||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.Uninterruptibles;
|
import com.google.common.util.concurrent.Uninterruptibles;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.joda.time.ReadableDuration;
|
import org.joda.time.ReadableDuration;
|
||||||
|
|
||||||
/** Implementation of {@link Sleeper} for production use. */
|
/** Implementation of {@link Sleeper} for production use. */
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ public class TextDiffSubject extends Subject {
|
|||||||
|
|
||||||
private final ImmutableList<String> actual;
|
private final ImmutableList<String> actual;
|
||||||
private DiffFormat diffFormat = DiffFormat.SIDE_BY_SIDE_MARKDOWN;
|
private DiffFormat diffFormat = DiffFormat.SIDE_BY_SIDE_MARKDOWN;
|
||||||
|
private ImmutableList<String> comments = ImmutableList.of();
|
||||||
|
|
||||||
protected TextDiffSubject(FailureMetadata metadata, List<String> actual) {
|
protected TextDiffSubject(FailureMetadata metadata, List<String> actual) {
|
||||||
super(metadata, actual);
|
super(metadata, actual);
|
||||||
@@ -83,10 +84,22 @@ public class TextDiffSubject extends Subject {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** If set, ignore lines that start with the given string. */
|
||||||
|
public TextDiffSubject ignoringLinesStartingWith(String... comments) {
|
||||||
|
this.comments = ImmutableList.copyOf(comments);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableList<String> filterComments(List<String> lines) {
|
||||||
|
return lines.stream()
|
||||||
|
.filter(line -> comments.stream().noneMatch(line::startsWith))
|
||||||
|
.collect(ImmutableList.toImmutableList());
|
||||||
|
}
|
||||||
|
|
||||||
public void hasSameContentAs(List<String> expectedContent) {
|
public void hasSameContentAs(List<String> expectedContent) {
|
||||||
checkNotNull(expectedContent, "expectedContent");
|
checkNotNull(expectedContent, "expectedContent");
|
||||||
ImmutableList<String> expected = ImmutableList.copyOf(expectedContent);
|
ImmutableList<String> expected = filterComments(expectedContent);
|
||||||
if (expected.equals(actual)) {
|
if (filterComments(expected).equals(filterComments(actual))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String diffString = diffFormat.generateDiff(expected, actual);
|
String diffString = diffFormat.generateDiff(expected, actual);
|
||||||
|
|||||||
+13
-5
@@ -25,7 +25,11 @@ import textwrap
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
# We should never analyze any generated files
|
# We should never analyze any generated files
|
||||||
UNIVERSALLY_SKIPPED_PATTERNS = {"/build/", "cloudbuild-caches", "/out/", ".git/", ".gradle/", "/dist/", "karma.conf.js", "polyfills.ts", "test.ts", "/docs/console-endpoints/"}
|
UNIVERSALLY_SKIPPED_PATTERNS = {"/build/", "cloudbuild-caches", "/out/", ".git/",
|
||||||
|
".gradle/", "/dist/", "/console-alpha/", "/console-crash/", "/console-qa",
|
||||||
|
"/console-sandbox", "/console-production", "karma.conf.js", "polyfills.ts",
|
||||||
|
"test.ts", "/docs/console-endpoints/", "/bin/generated-sources/",
|
||||||
|
"/bin/generated-test-sources/", "src/main/generated", "src/test/generated"}
|
||||||
# We can't rely on CI to have the Enum package installed so we do this instead.
|
# We can't rely on CI to have the Enum package installed so we do this instead.
|
||||||
FORBIDDEN = 1
|
FORBIDDEN = 1
|
||||||
REQUIRED = 2
|
REQUIRED = 2
|
||||||
@@ -87,11 +91,9 @@ PRESUBMITS = {
|
|||||||
PresubmitCheck(
|
PresubmitCheck(
|
||||||
r".*Copyright 20\d{2} The Nomulus Authors\. All Rights Reserved\.",
|
r".*Copyright 20\d{2} The Nomulus Authors\. All Rights Reserved\.",
|
||||||
("java", "js", "soy", "sql", "py", "sh", "gradle", "ts"), {
|
("java", "js", "soy", "sql", "py", "sh", "gradle", "ts"), {
|
||||||
".git", "/build/", "/bin/generated-sources/", "/bin/generated-test-sources/",
|
".git", "/build/", "node_modules/", "LoggerConfig.java", "registrar_bin.",
|
||||||
"node_modules/", "LoggerConfig.java", "registrar_bin.",
|
|
||||||
"registrar_dbg.", "google-java-format-diff.py",
|
"registrar_dbg.", "google-java-format-diff.py",
|
||||||
"nomulus.golden.sql", "soyutils_usegoog.js", "javascript/checks.js",
|
"nomulus.golden.sql", "soyutils_usegoog.js", "javascript/checks.js"
|
||||||
"/src/main/generated", "/src/test/generated"
|
|
||||||
}, REQUIRED):
|
}, REQUIRED):
|
||||||
"File did not include the license header.",
|
"File did not include the license header.",
|
||||||
|
|
||||||
@@ -208,6 +210,12 @@ PRESUBMITS = {
|
|||||||
{"/node_modules/"},
|
{"/node_modules/"},
|
||||||
):
|
):
|
||||||
"Do not use javax.servlet.* Use jakarta.servlet.* instead.",
|
"Do not use javax.servlet.* Use jakarta.servlet.* instead.",
|
||||||
|
PresubmitCheck(
|
||||||
|
r".*javax\.inject\..*",
|
||||||
|
"java",
|
||||||
|
{"/node_modules/"},
|
||||||
|
):
|
||||||
|
"Do not use javax.inject.* Use jakarta.inject.* instead.",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Note that this regex only works for one kind of Flyway file. If we want to
|
# Note that this regex only works for one kind of Flyway file. If we want to
|
||||||
|
|||||||
@@ -44,3 +44,4 @@ Thumbs.db
|
|||||||
|
|
||||||
# Build artifact
|
# Build artifact
|
||||||
/staged/dist
|
/staged/dist
|
||||||
|
/staged/console-*
|
||||||
|
|||||||
@@ -96,11 +96,17 @@
|
|||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"namedChunks": true
|
"namedChunks": true
|
||||||
},
|
},
|
||||||
"development": {
|
"qa": {
|
||||||
"optimization": false,
|
"optimization": false,
|
||||||
"extractLicenses": false,
|
"extractLicenses": false,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"namedChunks": true
|
"namedChunks": true
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"optimization": false,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"namedChunks": true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultConfiguration": "production"
|
"defaultConfiguration": "production"
|
||||||
@@ -120,6 +126,9 @@
|
|||||||
"sandbox": {
|
"sandbox": {
|
||||||
"buildTarget": "console-webapp:build:sandbox"
|
"buildTarget": "console-webapp:build:sandbox"
|
||||||
},
|
},
|
||||||
|
"qa": {
|
||||||
|
"buildTarget": "console-webapp:build:qa"
|
||||||
|
},
|
||||||
"development": {
|
"development": {
|
||||||
"buildTarget": "console-webapp:build:development"
|
"buildTarget": "console-webapp:build:development"
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-10
@@ -40,15 +40,55 @@ task runConsoleWebappUnitTests(type: Exec) {
|
|||||||
task buildConsoleWebapp(type: Exec) {
|
task buildConsoleWebapp(type: Exec) {
|
||||||
workingDir "${consoleDir}/"
|
workingDir "${consoleDir}/"
|
||||||
executable 'npm'
|
executable 'npm'
|
||||||
def configuration = project.hasProperty('configuration') ?
|
def configuration = project.getProperty('configuration')
|
||||||
project.getProperty('configuration') :
|
|
||||||
'production'
|
|
||||||
args 'run', "build", "--configuration=${configuration}"
|
args 'run', "build", "--configuration=${configuration}"
|
||||||
doFirst {
|
doFirst {
|
||||||
println "Building console for environment: ${configuration}"
|
println "Building console for environment: ${configuration}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task buildConsoleForAll() {}
|
||||||
|
|
||||||
|
def createConsoleTask = { env ->
|
||||||
|
project.tasks.register("buildConsoleFor${env.capitalize()}", Exec) {
|
||||||
|
workingDir "${consoleDir}/"
|
||||||
|
executable 'npm'
|
||||||
|
args 'run', 'build', "--configuration=${env}"
|
||||||
|
doFirst {
|
||||||
|
println "Building console for environment: ${env}"
|
||||||
|
}
|
||||||
|
doLast {
|
||||||
|
copy {
|
||||||
|
from "${consoleDir}/staged/dist/"
|
||||||
|
into "${consoleDir}/staged/console-${env}"
|
||||||
|
}
|
||||||
|
delete "${consoleDir}/staged/dist"
|
||||||
|
}
|
||||||
|
dependsOn(tasks.npmInstallDeps)
|
||||||
|
}
|
||||||
|
project.tasks.register("deleteConsoleFor${env.capitalize()}", Delete) {
|
||||||
|
delete "${consoleDir}/staged/console-${env}"
|
||||||
|
}
|
||||||
|
tasks.named('clean') {
|
||||||
|
dependsOn(tasks.named("deleteConsoleFor${env.capitalize()}"))
|
||||||
|
}
|
||||||
|
tasks.named('buildConsoleForAll') {
|
||||||
|
dependsOn(tasks.named("buildConsoleFor${env.capitalize()}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
['alpha', 'crash', 'qa', 'sandbox', 'production'].forEach {env ->
|
||||||
|
createConsoleTask(env)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force an order so we don't run these tasks in parallel.
|
||||||
|
tasks.buildConsoleForCrash.mustRunAfter(tasks.buildConsoleForAlpha)
|
||||||
|
tasks.buildConsoleForQa.mustRunAfter(tasks.buildConsoleForCrash)
|
||||||
|
tasks.buildConsoleForSandbox.mustRunAfter(tasks.buildConsoleForQa)
|
||||||
|
tasks.buildConsoleForProduction.mustRunAfter(tasks.buildConsoleForSandbox)
|
||||||
|
// This task must run last, otherwise the previous tasks will have deleted the "dist" folder.
|
||||||
|
tasks.buildConsoleWebapp.mustRunAfter(tasks.buildConsoleForProduction)
|
||||||
|
|
||||||
task applyFormatting(type: Exec) {
|
task applyFormatting(type: Exec) {
|
||||||
workingDir "${consoleDir}/"
|
workingDir "${consoleDir}/"
|
||||||
executable 'npm'
|
executable 'npm'
|
||||||
@@ -61,16 +101,9 @@ task checkFormatting(type: Exec) {
|
|||||||
args 'run', 'prettify:check'
|
args 'run', 'prettify:check'
|
||||||
}
|
}
|
||||||
|
|
||||||
task deploy(type: Exec) {
|
|
||||||
workingDir "${consoleDir}/staged"
|
|
||||||
executable 'gcloud'
|
|
||||||
args 'app', 'deploy', "${projectParam}", '--quiet'
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.buildConsoleWebapp.dependsOn(tasks.npmInstallDeps)
|
tasks.buildConsoleWebapp.dependsOn(tasks.npmInstallDeps)
|
||||||
tasks.runConsoleWebappUnitTests.dependsOn(tasks.npmInstallDeps)
|
tasks.runConsoleWebappUnitTests.dependsOn(tasks.npmInstallDeps)
|
||||||
tasks.applyFormatting.dependsOn(tasks.npmInstallDeps)
|
tasks.applyFormatting.dependsOn(tasks.npmInstallDeps)
|
||||||
tasks.checkFormatting.dependsOn(tasks.npmInstallDeps)
|
tasks.checkFormatting.dependsOn(tasks.npmInstallDeps)
|
||||||
tasks.build.dependsOn(tasks.checkFormatting)
|
tasks.build.dependsOn(tasks.checkFormatting)
|
||||||
tasks.build.dependsOn(tasks.runConsoleWebappUnitTests)
|
tasks.build.dependsOn(tasks.runConsoleWebappUnitTests)
|
||||||
tasks.deploy.dependsOn(tasks.buildConsoleWebapp)
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
configuration=production
|
||||||
Generated
+5859
-6404
File diff suppressed because it is too large
Load Diff
+21
-21
@@ -16,31 +16,31 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^18.0.2",
|
"@angular/animations": "^19.1.4",
|
||||||
"@angular/cdk": "^18.0.2",
|
"@angular/cdk": "^19.1.2",
|
||||||
"@angular/common": "^18.0.2",
|
"@angular/common": "^19.1.4",
|
||||||
"@angular/compiler": "^18.0.2",
|
"@angular/compiler": "^19.1.4",
|
||||||
"@angular/core": "^18.0.2",
|
"@angular/core": "^19.1.4",
|
||||||
"@angular/forms": "^18.0.2",
|
"@angular/forms": "^19.1.4",
|
||||||
"@angular/material": "^18.0.2",
|
"@angular/material": "^19.1.2",
|
||||||
"@angular/platform-browser": "^18.0.2",
|
"@angular/platform-browser": "^19.1.4",
|
||||||
"@angular/platform-browser-dynamic": "^18.0.2",
|
"@angular/platform-browser-dynamic": "^19.1.4",
|
||||||
"@angular/router": "^18.0.2",
|
"@angular/router": "^19.1.4",
|
||||||
"rxjs": "~7.5.0",
|
"rxjs": "~7.5.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.14.2"
|
"zone.js": "~0.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^18.0.3",
|
"@angular-devkit/build-angular": "^19.1.5",
|
||||||
"@angular-eslint/builder": "18.0.1",
|
"@angular-eslint/builder": "19.0.2",
|
||||||
"@angular-eslint/eslint-plugin": "18.0.1",
|
"@angular-eslint/eslint-plugin": "19.0.2",
|
||||||
"@angular-eslint/eslint-plugin-template": "18.0.1",
|
"@angular-eslint/eslint-plugin-template": "19.0.2",
|
||||||
"@angular-eslint/schematics": "18.0.1",
|
"@angular-eslint/schematics": "19.0.2",
|
||||||
"@angular-eslint/template-parser": "18.0.1",
|
"@angular-eslint/template-parser": "19.0.2",
|
||||||
"@angular/cli": "~18.0.3",
|
"@angular/cli": "~19.1.5",
|
||||||
"@angular/compiler-cli": "^18.0.2",
|
"@angular/compiler-cli": "^19.1.4",
|
||||||
"@types/jasmine": "~4.0.0",
|
"@types/jasmine": "~4.0.0",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^18.19.74",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||||
"@typescript-eslint/parser": "^7.2.0",
|
"@typescript-eslint/parser": "^7.2.0",
|
||||||
"concurrently": "^7.6.0",
|
"concurrently": "^7.6.0",
|
||||||
@@ -52,6 +52,6 @@
|
|||||||
"karma-jasmine": "~5.1.0",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "~2.0.0",
|
"karma-jasmine-html-reporter": "~2.0.0",
|
||||||
"prettier": "2.8.7",
|
"prettier": "2.8.7",
|
||||||
"typescript": "~5.4.5"
|
"typescript": "^5.7.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import { ResourcesComponent } from './resources/resources.component';
|
|||||||
import ContactComponent from './settings/contact/contact.component';
|
import ContactComponent from './settings/contact/contact.component';
|
||||||
import SecurityComponent from './settings/security/security.component';
|
import SecurityComponent from './settings/security/security.component';
|
||||||
import { SettingsComponent } from './settings/settings.component';
|
import { SettingsComponent } from './settings/settings.component';
|
||||||
import WhoisComponent from './settings/whois/whois.component';
|
|
||||||
import { SupportComponent } from './support/support.component';
|
import { SupportComponent } from './support/support.component';
|
||||||
|
import RdapComponent from './settings/rdap/rdap.component';
|
||||||
|
|
||||||
export interface RouteWithIcon extends Route {
|
export interface RouteWithIcon extends Route {
|
||||||
iconName?: string;
|
iconName?: string;
|
||||||
@@ -83,9 +83,9 @@ export const routes: RouteWithIcon[] = [
|
|||||||
title: 'Contacts',
|
title: 'Contacts',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: WhoisComponent.PATH,
|
path: RdapComponent.PATH,
|
||||||
component: WhoisComponent,
|
component: RdapComponent,
|
||||||
title: 'WHOIS Info',
|
title: 'RDAP Info',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: SecurityComponent.PATH,
|
path: SecurityComponent.PATH,
|
||||||
|
|||||||
@@ -7,18 +7,19 @@
|
|||||||
></mat-progress-bar>
|
></mat-progress-bar>
|
||||||
</div>
|
</div>
|
||||||
<mat-sidenav-container class="console-app__container">
|
<mat-sidenav-container class="console-app__container">
|
||||||
|
<mat-sidenav-content class="console-app__content-wrapper">
|
||||||
|
<div class="console-app__content" role="main">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
</mat-sidenav-content>
|
||||||
<mat-sidenav
|
<mat-sidenav
|
||||||
[mode]="breakpointObserver.isMobileView() ? 'over' : 'side'"
|
[mode]="breakpointObserver.isMobileView() ? 'over' : 'side'"
|
||||||
[opened]="!breakpointObserver.isMobileView()"
|
[opened]="!breakpointObserver.isMobileView()"
|
||||||
|
[disableClose]="!breakpointObserver.isMobileView()"
|
||||||
#sidenav
|
#sidenav
|
||||||
class="console-app__sidebar"
|
class="console-app__sidebar"
|
||||||
>
|
>
|
||||||
<app-navigation />
|
<app-navigation />
|
||||||
</mat-sidenav>
|
</mat-sidenav>
|
||||||
<mat-sidenav-content class="console-app__content-wrapper">
|
|
||||||
<div class="console-app__content">
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
</div>
|
|
||||||
</mat-sidenav-content>
|
|
||||||
</mat-sidenav-container>
|
</mat-sidenav-container>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,12 +20,18 @@ import { AppComponent } from './app.component';
|
|||||||
import { MaterialModule } from './material.module';
|
import { MaterialModule } from './material.module';
|
||||||
import { BackendService } from './shared/services/backend.service';
|
import { BackendService } from './shared/services/backend.service';
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
describe('AppComponent', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [AppComponent],
|
declarations: [AppComponent],
|
||||||
imports: [MaterialModule, BrowserAnimationsModule, AppRoutingModule],
|
imports: [
|
||||||
|
MaterialModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
AppRoutingModule,
|
||||||
|
AppModule,
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
BackendService,
|
BackendService,
|
||||||
provideHttpClient(),
|
provideHttpClient(),
|
||||||
@@ -36,6 +42,7 @@ describe('AppComponent', () => {
|
|||||||
|
|
||||||
it('should create the app', () => {
|
it('should create the app', () => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
const app = fixture.componentInstance;
|
const app = fixture.componentInstance;
|
||||||
expect(app).toBeTruthy();
|
expect(app).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { UserDataService } from './shared/services/userData.service';
|
|||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.scss'],
|
styleUrls: ['./app.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class AppComponent implements AfterViewInit {
|
export class AppComponent implements AfterViewInit {
|
||||||
@ViewChild(MatSidenav)
|
@ViewChild(MatSidenav)
|
||||||
|
|||||||
@@ -26,7 +26,11 @@ import { BackendService } from './shared/services/backend.service';
|
|||||||
import { provideHttpClient } from '@angular/common/http';
|
import { provideHttpClient } from '@angular/common/http';
|
||||||
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
|
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
|
||||||
import { BillingInfoComponent } from './billingInfo/billingInfo.component';
|
import { BillingInfoComponent } from './billingInfo/billingInfo.component';
|
||||||
import { DomainListComponent } from './domains/domainList.component';
|
import {
|
||||||
|
DomainListComponent,
|
||||||
|
ReasonDialogComponent,
|
||||||
|
ResponseDialogComponent,
|
||||||
|
} from './domains/domainList.component';
|
||||||
import { RegistryLockComponent } from './domains/registryLock.component';
|
import { RegistryLockComponent } from './domains/registryLock.component';
|
||||||
import { HeaderComponent } from './header/header.component';
|
import { HeaderComponent } from './header/header.component';
|
||||||
import { HomeComponent } from './home/home.component';
|
import { HomeComponent } from './home/home.component';
|
||||||
@@ -43,8 +47,6 @@ import EppPasswordEditComponent from './settings/security/eppPasswordEdit.compon
|
|||||||
import SecurityComponent from './settings/security/security.component';
|
import SecurityComponent from './settings/security/security.component';
|
||||||
import SecurityEditComponent from './settings/security/securityEdit.component';
|
import SecurityEditComponent from './settings/security/securityEdit.component';
|
||||||
import { SettingsComponent } from './settings/settings.component';
|
import { SettingsComponent } from './settings/settings.component';
|
||||||
import WhoisComponent from './settings/whois/whois.component';
|
|
||||||
import WhoisEditComponent from './settings/whois/whoisEdit.component';
|
|
||||||
import { NotificationsComponent } from './shared/components/notifications/notifications.component';
|
import { NotificationsComponent } from './shared/components/notifications/notifications.component';
|
||||||
import { SelectedRegistrarWrapper } from './shared/components/selectedRegistrarWrapper/selectedRegistrarWrapper.component';
|
import { SelectedRegistrarWrapper } from './shared/components/selectedRegistrarWrapper/selectedRegistrarWrapper.component';
|
||||||
import { LocationBackDirective } from './shared/directives/locationBack.directive';
|
import { LocationBackDirective } from './shared/directives/locationBack.directive';
|
||||||
@@ -55,6 +57,9 @@ import { UserDataService } from './shared/services/userData.service';
|
|||||||
import { SnackBarModule } from './snackbar.module';
|
import { SnackBarModule } from './snackbar.module';
|
||||||
import { SupportComponent } from './support/support.component';
|
import { SupportComponent } from './support/support.component';
|
||||||
import { TldsComponent } from './tlds/tlds.component';
|
import { TldsComponent } from './tlds/tlds.component';
|
||||||
|
import { ForceFocusDirective } from './shared/directives/forceFocus.directive';
|
||||||
|
import RdapComponent from './settings/rdap/rdap.component';
|
||||||
|
import RdapEditComponent from './settings/rdap/rdapEdit.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [SelectedRegistrarWrapper],
|
declarations: [SelectedRegistrarWrapper],
|
||||||
@@ -71,27 +76,30 @@ export class SelectedRegistrarModule {}
|
|||||||
ContactDetailsComponent,
|
ContactDetailsComponent,
|
||||||
DomainListComponent,
|
DomainListComponent,
|
||||||
EppPasswordEditComponent,
|
EppPasswordEditComponent,
|
||||||
|
ForceFocusDirective,
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
HomeComponent,
|
HomeComponent,
|
||||||
LocationBackDirective,
|
LocationBackDirective,
|
||||||
UserLevelVisibility,
|
|
||||||
NavigationComponent,
|
NavigationComponent,
|
||||||
NewRegistrarComponent,
|
NewRegistrarComponent,
|
||||||
NotificationsComponent,
|
NotificationsComponent,
|
||||||
|
RdapComponent,
|
||||||
|
RdapEditComponent,
|
||||||
|
ReasonDialogComponent,
|
||||||
RegistrarComponent,
|
RegistrarComponent,
|
||||||
RegistrarDetailsComponent,
|
RegistrarDetailsComponent,
|
||||||
RegistryLockComponent,
|
|
||||||
RegistrarSelectorComponent,
|
RegistrarSelectorComponent,
|
||||||
|
RegistryLockComponent,
|
||||||
RegistryLockVerifyComponent,
|
RegistryLockVerifyComponent,
|
||||||
ResourcesComponent,
|
ResourcesComponent,
|
||||||
|
ResponseDialogComponent,
|
||||||
SecurityComponent,
|
SecurityComponent,
|
||||||
SecurityEditComponent,
|
SecurityEditComponent,
|
||||||
SettingsComponent,
|
SettingsComponent,
|
||||||
SettingsContactComponent,
|
SettingsContactComponent,
|
||||||
SupportComponent,
|
SupportComponent,
|
||||||
TldsComponent,
|
TldsComponent,
|
||||||
WhoisComponent,
|
UserLevelVisibility,
|
||||||
WhoisEditComponent,
|
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
imports: [
|
imports: [
|
||||||
@@ -100,8 +108,8 @@ export class SelectedRegistrarModule {}
|
|||||||
BrowserModule,
|
BrowserModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
MaterialModule,
|
MaterialModule,
|
||||||
SnackBarModule,
|
|
||||||
SelectedRegistrarModule,
|
SelectedRegistrarModule,
|
||||||
|
SnackBarModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
BackendService,
|
BackendService,
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
<app-selected-registrar-wrapper>
|
<app-selected-registrar-wrapper>
|
||||||
<h1 class="mat-headline-4">Billing Info</h1>
|
<h1 class="mat-headline-4" forceFocus>Billing Info</h1>
|
||||||
<div class="console-app__billing">
|
<div class="console-app__billing">
|
||||||
<div>
|
<div>
|
||||||
<div class="console-app__billing-subhead">
|
<div class="console-app__billing-subhead">
|
||||||
Billing records and information
|
Billing records and information
|
||||||
</div>
|
</div>
|
||||||
<a class="text-l" href="{{ driveFolderUrl() }}" target="_blank"
|
<a
|
||||||
|
class="text-l"
|
||||||
|
href="{{ driveFolderUrl() }}"
|
||||||
|
target="_blank"
|
||||||
|
aria-label="View billing records on Google Drive"
|
||||||
>View on Google Drive</a
|
>View on Google Drive</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<img src="./assets/billing.png" />
|
<img src="./assets/billing.png" alt="Generic billing image" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</app-selected-registrar-wrapper>
|
</app-selected-registrar-wrapper>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
&-subhead {
|
&-subhead {
|
||||||
font-size: 20px;
|
font-size: 1.25rem;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
|||||||
selector: 'app-billingInfo',
|
selector: 'app-billingInfo',
|
||||||
templateUrl: './billingInfo.component.html',
|
templateUrl: './billingInfo.component.html',
|
||||||
styleUrls: ['./billingInfo.component.scss'],
|
styleUrls: ['./billingInfo.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class BillingInfoComponent {
|
export class BillingInfoComponent {
|
||||||
public static PATH = 'billingInfo';
|
public static PATH = 'billingInfo';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<app-selected-registrar-wrapper>
|
<app-selected-registrar-wrapper>
|
||||||
<div class="console-app-domains">
|
<div class="console-app-domains">
|
||||||
<h1 class="mat-headline-4">Domains</h1>
|
<h1 class="mat-headline-4" forceFocus>Domains</h1>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="console-app-domains__actions-wrapper"
|
class="console-app-domains__actions-wrapper"
|
||||||
@@ -24,11 +24,37 @@
|
|||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<mat-menu #actions="matMenu">
|
<mat-menu #actions="matMenu">
|
||||||
<ng-template matMenuContent let-domainName="domainName">
|
<ng-template
|
||||||
<button mat-menu-item (click)="openRegistryLock(domainName)">
|
matMenuContent
|
||||||
|
let-domainName="domainName"
|
||||||
|
let-domain="domain"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
mat-menu-item
|
||||||
|
(click)="openRegistryLock(domainName)"
|
||||||
|
aria-label="Access registry lock for domain"
|
||||||
|
>
|
||||||
<mat-icon>key</mat-icon>
|
<mat-icon>key</mat-icon>
|
||||||
<span>Registry Lock</span>
|
<span>Registry Lock</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
mat-menu-item
|
||||||
|
(click)="onSuspendClick(domainName)"
|
||||||
|
[elementId]="getElementIdForSuspendUnsuspend()"
|
||||||
|
[disabled]="isDomainUnsuspendable(domain)"
|
||||||
|
>
|
||||||
|
<mat-icon>lock_clock</mat-icon>
|
||||||
|
<span>Suspend</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
mat-menu-item
|
||||||
|
(click)="onUnsuspendClick(domainName)"
|
||||||
|
[elementId]="getElementIdForSuspendUnsuspend()"
|
||||||
|
[disabled]="!isDomainUnsuspendable(domain)"
|
||||||
|
>
|
||||||
|
<mat-icon>lock_open</mat-icon>
|
||||||
|
<span>Unsuspend</span>
|
||||||
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
<div
|
<div
|
||||||
@@ -65,16 +91,67 @@
|
|||||||
/>
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="console-app__domains-selection"
|
||||||
|
[elementId]="getElementIdForBulkDelete()"
|
||||||
|
[ngClass]="{ active: selection.hasValue() }"
|
||||||
|
>
|
||||||
|
<div class="console-app__domains-selection-text">
|
||||||
|
{{ selection.selected.length }} Selected
|
||||||
|
</div>
|
||||||
|
<div class="console-app__domains-selection-actions">
|
||||||
|
<button
|
||||||
|
mat-flat-button
|
||||||
|
aria-label="Delete Selected Domains"
|
||||||
|
[attr.aria-hidden]="!selection.hasValue()"
|
||||||
|
(click)="deleteSelectedDomains()"
|
||||||
|
>
|
||||||
|
Delete Selected Domains
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<mat-table
|
<mat-table
|
||||||
[dataSource]="dataSource"
|
[dataSource]="dataSource"
|
||||||
class="mat-elevation-z0"
|
class="mat-elevation-z0"
|
||||||
class="console-app__domains-table"
|
class="console-app__domains-table"
|
||||||
>
|
>
|
||||||
|
<!-- Checkbox Column -->
|
||||||
|
<ng-container matColumnDef="select">
|
||||||
|
<mat-header-cell *matHeaderCellDef>
|
||||||
|
<mat-checkbox
|
||||||
|
(change)="$event ? toggleAllRows() : null"
|
||||||
|
[checked]="selection.hasValue() && isAllSelected"
|
||||||
|
[indeterminate]="selection.hasValue() && !isAllSelected"
|
||||||
|
[aria-label]="checkboxLabel()"
|
||||||
|
[elementId]="getElementIdForBulkDelete()"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row">
|
||||||
|
<mat-checkbox
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
(change)="$event ? selection.toggle(row) : null"
|
||||||
|
[checked]="selection.isSelected(row)"
|
||||||
|
[aria-label]="checkboxLabel(row)"
|
||||||
|
[elementId]="getElementIdForBulkDelete()"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="domainName">
|
<ng-container matColumnDef="domainName">
|
||||||
<mat-header-cell *matHeaderCellDef>Domain Name</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef>Domain Name</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let element">{{
|
<mat-cell *matCellDef="let element">
|
||||||
element.domainName
|
<mat-icon
|
||||||
}}</mat-cell>
|
*ngIf="getOperationMessage(element.domainName)"
|
||||||
|
[matTooltip]="getOperationMessage(element.domainName)"
|
||||||
|
matTooltipPosition="above"
|
||||||
|
class="primary-text"
|
||||||
|
>info</mat-icon
|
||||||
|
>
|
||||||
|
<span>{{ element.domainName }}</span>
|
||||||
|
</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="creationTime">
|
<ng-container matColumnDef="creationTime">
|
||||||
@@ -115,7 +192,10 @@
|
|||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
[matMenuTriggerFor]="actions"
|
[matMenuTriggerFor]="actions"
|
||||||
[matMenuTriggerData]="{ domainName: element.domainName }"
|
[matMenuTriggerData]="{
|
||||||
|
domainName: element.domainName,
|
||||||
|
domain: element
|
||||||
|
}"
|
||||||
aria-label="Domain actions"
|
aria-label="Domain actions"
|
||||||
>
|
>
|
||||||
<mat-icon>more_horiz</mat-icon>
|
<mat-icon>more_horiz</mat-icon>
|
||||||
|
|||||||
@@ -12,6 +12,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__domains-selection {
|
||||||
|
height: 60px;
|
||||||
|
max-height: 0;
|
||||||
|
transition: max-height 0.2s linear;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
gap: 20px;
|
||||||
|
&-text {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
max-height: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-domains__download {
|
&-domains__download {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -55px;
|
top: -55px;
|
||||||
@@ -41,6 +57,22 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
.mat-column-select {
|
||||||
|
max-width: 60px;
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
.mat-column-domainName {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 25px;
|
||||||
|
mat-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mat-cell:has([style*="display: none"]),
|
||||||
|
mat-header-cell:has([style*="display: none"]) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__domains-spinner {
|
&__domains-spinner {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { MaterialModule } from '../material.module';
|
|||||||
import { BackendService } from '../shared/services/backend.service';
|
import { BackendService } from '../shared/services/backend.service';
|
||||||
import { DomainListComponent } from './domainList.component';
|
import { DomainListComponent } from './domainList.component';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { AppModule } from '../app.module';
|
||||||
|
|
||||||
describe('DomainListComponent', () => {
|
describe('DomainListComponent', () => {
|
||||||
let component: DomainListComponent;
|
let component: DomainListComponent;
|
||||||
@@ -29,7 +30,12 @@ describe('DomainListComponent', () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [DomainListComponent],
|
declarations: [DomainListComponent],
|
||||||
imports: [MaterialModule, BrowserAnimationsModule, FormsModule],
|
imports: [
|
||||||
|
MaterialModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
FormsModule,
|
||||||
|
AppModule,
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
BackendService,
|
BackendService,
|
||||||
provideHttpClient(),
|
provideHttpClient(),
|
||||||
|
|||||||
@@ -12,27 +12,123 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { SelectionModel } from '@angular/cdk/collections';
|
||||||
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
|
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
|
||||||
import { Component, ViewChild, effect } from '@angular/core';
|
import { Component, effect, Inject, ViewChild } from '@angular/core';
|
||||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
import { MatTableDataSource } from '@angular/material/table';
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
import { Subject, debounceTime } from 'rxjs';
|
import { debounceTime, filter, Subject, take } from 'rxjs';
|
||||||
import { RegistrarService } from '../registrar/registrar.service';
|
import { RegistrarService } from '../registrar/registrar.service';
|
||||||
import { Domain, DomainListService } from './domainList.service';
|
import {
|
||||||
|
BULK_ACTION_NAME,
|
||||||
|
Domain,
|
||||||
|
DomainListService,
|
||||||
|
} from './domainList.service';
|
||||||
import { RegistryLockComponent } from './registryLock.component';
|
import { RegistryLockComponent } from './registryLock.component';
|
||||||
import { RegistryLockService } from './registryLock.service';
|
import { RegistryLockService } from './registryLock.service';
|
||||||
|
import {
|
||||||
|
MAT_DIALOG_DATA,
|
||||||
|
MatDialog,
|
||||||
|
MatDialogRef,
|
||||||
|
} from '@angular/material/dialog';
|
||||||
|
import { RESTRICTED_ELEMENTS } from '../shared/directives/userLevelVisiblity.directive';
|
||||||
|
|
||||||
|
interface DomainResponse {
|
||||||
|
message: string;
|
||||||
|
responseCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DomainData {
|
||||||
|
[domain: string]: DomainResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-response-dialog',
|
||||||
|
template: `
|
||||||
|
<h2 mat-dialog-title>{{ data.title }}</h2>
|
||||||
|
<mat-dialog-content [innerHTML]="data.content" />
|
||||||
|
<mat-dialog-actions>
|
||||||
|
<button mat-button (click)="onClose()">Close</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
|
`,
|
||||||
|
standalone: false,
|
||||||
|
})
|
||||||
|
export class ResponseDialogComponent {
|
||||||
|
constructor(
|
||||||
|
public dialogRef: MatDialogRef<ReasonDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA)
|
||||||
|
public data: { title: string; content: string }
|
||||||
|
) {}
|
||||||
|
|
||||||
|
onClose(): void {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Operation {
|
||||||
|
deleting = 'deleting',
|
||||||
|
suspending = 'suspending',
|
||||||
|
unsuspending = 'unsuspending',
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-reason-dialog',
|
||||||
|
template: `
|
||||||
|
<h2 mat-dialog-title>
|
||||||
|
Please provide the (EPP) reason for {{ data.operation }} the domain(s):
|
||||||
|
</h2>
|
||||||
|
<mat-dialog-content>
|
||||||
|
<mat-form-field appearance="outline" style="width:100%">
|
||||||
|
<textarea matInput [(ngModel)]="reason" rows="4"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-dialog-content>
|
||||||
|
<mat-dialog-actions>
|
||||||
|
<button mat-button (click)="onCancel()">Cancel</button>
|
||||||
|
<button mat-button color="warn" (click)="onSave()" [disabled]="!reason">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
|
`,
|
||||||
|
standalone: false,
|
||||||
|
})
|
||||||
|
export class ReasonDialogComponent {
|
||||||
|
reason: string = '';
|
||||||
|
constructor(
|
||||||
|
public dialogRef: MatDialogRef<ReasonDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA)
|
||||||
|
public data: { operation: Operation }
|
||||||
|
) {}
|
||||||
|
|
||||||
|
onSave(): void {
|
||||||
|
this.dialogRef.close(this.reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel(): void {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-domain-list',
|
selector: 'app-domain-list',
|
||||||
templateUrl: './domainList.component.html',
|
templateUrl: './domainList.component.html',
|
||||||
styleUrls: ['./domainList.component.scss'],
|
styleUrls: ['./domainList.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class DomainListComponent {
|
export class DomainListComponent {
|
||||||
public static PATH = 'domain-list';
|
public static PATH = 'domain-list';
|
||||||
|
private static SUSPENDED_STATUSES = [
|
||||||
|
'SERVER_RENEW_PROHIBITED',
|
||||||
|
'SERVER_TRANSFER_PROHIBITED',
|
||||||
|
'SERVER_UPDATE_PROHIBITED',
|
||||||
|
'SERVER_DELETE_PROHIBITED',
|
||||||
|
'SERVER_HOLD',
|
||||||
|
];
|
||||||
private readonly DEBOUNCE_MS = 500;
|
private readonly DEBOUNCE_MS = 500;
|
||||||
|
isAllSelected = false;
|
||||||
|
|
||||||
displayedColumns: string[] = [
|
displayedColumns: string[] = [
|
||||||
|
'select',
|
||||||
'domainName',
|
'domainName',
|
||||||
'creationTime',
|
'creationTime',
|
||||||
'registrationExpirationTime',
|
'registrationExpirationTime',
|
||||||
@@ -42,6 +138,7 @@ export class DomainListComponent {
|
|||||||
];
|
];
|
||||||
|
|
||||||
dataSource: MatTableDataSource<Domain> = new MatTableDataSource();
|
dataSource: MatTableDataSource<Domain> = new MatTableDataSource();
|
||||||
|
selection = new SelectionModel<Domain>(true, [], undefined, this.isChecked());
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
|
||||||
searchTermSubject = new Subject<string>();
|
searchTermSubject = new Subject<string>();
|
||||||
@@ -51,13 +148,18 @@ export class DomainListComponent {
|
|||||||
resultsPerPage = 50;
|
resultsPerPage = 50;
|
||||||
totalResults?: number = 0;
|
totalResults?: number = 0;
|
||||||
|
|
||||||
|
reason: string = '';
|
||||||
|
|
||||||
|
operationResult: DomainData | undefined;
|
||||||
|
|
||||||
@ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator;
|
@ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected domainListService: DomainListService,
|
protected domainListService: DomainListService,
|
||||||
protected registrarService: RegistrarService,
|
protected registrarService: RegistrarService,
|
||||||
protected registryLockService: RegistryLockService,
|
protected registryLockService: RegistryLockService,
|
||||||
private _snackBar: MatSnackBar
|
private _snackBar: MatSnackBar,
|
||||||
|
private dialog: MatDialog
|
||||||
) {
|
) {
|
||||||
effect(() => {
|
effect(() => {
|
||||||
this.pageNumber = 0;
|
this.pageNumber = 0;
|
||||||
@@ -134,6 +236,184 @@ export class DomainListComponent {
|
|||||||
onPageChange(event: PageEvent) {
|
onPageChange(event: PageEvent) {
|
||||||
this.pageNumber = event.pageIndex;
|
this.pageNumber = event.pageIndex;
|
||||||
this.resultsPerPage = event.pageSize;
|
this.resultsPerPage = event.pageSize;
|
||||||
|
this.selection.clear();
|
||||||
this.reloadData();
|
this.reloadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleAllRows() {
|
||||||
|
if (this.isAllSelected) {
|
||||||
|
this.selection.clear();
|
||||||
|
this.isAllSelected = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selection.select(...this.dataSource.data);
|
||||||
|
this.isAllSelected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkboxLabel(row?: Domain): string {
|
||||||
|
if (!row) {
|
||||||
|
return `${this.isAllSelected ? 'deselect' : 'select'} all`;
|
||||||
|
}
|
||||||
|
return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${
|
||||||
|
row.domainName
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isChecked(): ((o1: Domain, o2: Domain) => boolean) | undefined {
|
||||||
|
return (o1: Domain, o2: Domain) => {
|
||||||
|
if (!o1.domainName || !o2.domainName) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.isAllSelected || o1.domainName === o2.domainName;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getElementIdForBulkDelete() {
|
||||||
|
return RESTRICTED_ELEMENTS.BULK_DELETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
getElementIdForSuspendUnsuspend() {
|
||||||
|
return RESTRICTED_ELEMENTS.SUSPEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
getOperationMessage(domain: string) {
|
||||||
|
if (this.operationResult && this.operationResult[domain])
|
||||||
|
return this.operationResult[domain].message;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
isDomainUnsuspendable(domain: Domain) {
|
||||||
|
return DomainListComponent.SUSPENDED_STATUSES.every((s) =>
|
||||||
|
domain.statuses.includes(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendDeleteRequest(reason: string) {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.domainListService
|
||||||
|
.bulkDomainAction(
|
||||||
|
this.selection.selected.map((d) => d.domainName),
|
||||||
|
reason,
|
||||||
|
this.registrarService.registrarId(),
|
||||||
|
BULK_ACTION_NAME.DELETE
|
||||||
|
)
|
||||||
|
.pipe(take(1))
|
||||||
|
.subscribe({
|
||||||
|
next: (result: DomainData) => {
|
||||||
|
this.isLoading = false;
|
||||||
|
const successCount = Object.keys(result).filter((domainName) =>
|
||||||
|
result[domainName].responseCode.toString().startsWith('1')
|
||||||
|
).length;
|
||||||
|
const failureCount = Object.keys(result).length - successCount;
|
||||||
|
this.dialog.open(ResponseDialogComponent, {
|
||||||
|
data: {
|
||||||
|
title: 'Domain Deletion Results',
|
||||||
|
content: `Successfully deleted - ${successCount} domain(s)<br/>Failed to delete - ${failureCount} domain(s)<br/>${
|
||||||
|
failureCount
|
||||||
|
? 'Some domains could not be deleted due to ongoing processes or server errors. '
|
||||||
|
: ''
|
||||||
|
}Please check the table for more information.`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.selection.clear();
|
||||||
|
this.operationResult = result;
|
||||||
|
this.reloadData();
|
||||||
|
},
|
||||||
|
error: (err: HttpErrorResponse) => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this._snackBar.open(err.error || err.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteSelectedDomains() {
|
||||||
|
const dialogRef = this.dialog.open(ReasonDialogComponent, {
|
||||||
|
data: {
|
||||||
|
operation: Operation.deleting,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef
|
||||||
|
.afterClosed()
|
||||||
|
.pipe(
|
||||||
|
take(1),
|
||||||
|
filter((reason) => !!reason)
|
||||||
|
)
|
||||||
|
.subscribe(this.sendDeleteRequest.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
sendSuspendUnsuspendRequest(
|
||||||
|
domainName: string,
|
||||||
|
reason: string,
|
||||||
|
actionName: BULK_ACTION_NAME
|
||||||
|
) {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.domainListService
|
||||||
|
.bulkDomainAction(
|
||||||
|
[domainName],
|
||||||
|
reason,
|
||||||
|
this.registrarService.registrarId(),
|
||||||
|
actionName
|
||||||
|
)
|
||||||
|
.pipe(take(1))
|
||||||
|
.subscribe({
|
||||||
|
next: (result: DomainData) => {
|
||||||
|
this.isLoading = false;
|
||||||
|
if (result[domainName].responseCode.toString().startsWith('2')) {
|
||||||
|
this._snackBar.open(result[domainName].message);
|
||||||
|
} else {
|
||||||
|
this.reloadData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (err: HttpErrorResponse) => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this._snackBar.open(err.error || err.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onSuspendClick(domainName: string) {
|
||||||
|
const dialogRef = this.dialog.open(ReasonDialogComponent, {
|
||||||
|
data: {
|
||||||
|
operation: Operation.suspending,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef
|
||||||
|
.afterClosed()
|
||||||
|
.pipe(
|
||||||
|
take(1),
|
||||||
|
filter((reason) => !!reason)
|
||||||
|
)
|
||||||
|
.subscribe((reason) => {
|
||||||
|
this.sendSuspendUnsuspendRequest(
|
||||||
|
domainName,
|
||||||
|
reason,
|
||||||
|
BULK_ACTION_NAME.SUSPEND
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnsuspendClick(domainName: string) {
|
||||||
|
const dialogRef = this.dialog.open(ReasonDialogComponent, {
|
||||||
|
data: {
|
||||||
|
operation: Operation.unsuspending,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef
|
||||||
|
.afterClosed()
|
||||||
|
.pipe(
|
||||||
|
take(1),
|
||||||
|
filter((reason) => !!reason)
|
||||||
|
)
|
||||||
|
.subscribe((reason) => {
|
||||||
|
this.sendSuspendUnsuspendRequest(
|
||||||
|
domainName,
|
||||||
|
reason,
|
||||||
|
BULK_ACTION_NAME.UNSUSPEND
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ export interface DomainListResult {
|
|||||||
totalResults: number;
|
totalResults: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum BULK_ACTION_NAME {
|
||||||
|
DELETE = 'DELETE',
|
||||||
|
SUSPEND = 'SUSPEND',
|
||||||
|
UNSUSPEND = 'UNSUSPEND',
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
@@ -48,7 +54,6 @@ export class DomainListService {
|
|||||||
private backendService: BackendService,
|
private backendService: BackendService,
|
||||||
private registrarService: RegistrarService
|
private registrarService: RegistrarService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
retrieveDomains(
|
retrieveDomains(
|
||||||
pageNumber?: number,
|
pageNumber?: number,
|
||||||
resultsPerPage?: number,
|
resultsPerPage?: number,
|
||||||
@@ -71,4 +76,18 @@ export class DomainListService {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bulkDomainAction(
|
||||||
|
domains: string[],
|
||||||
|
reason: string,
|
||||||
|
registrarId: string,
|
||||||
|
actionName: BULK_ACTION_NAME
|
||||||
|
) {
|
||||||
|
return this.backendService.bulkDomainAction(
|
||||||
|
domains,
|
||||||
|
reason,
|
||||||
|
actionName,
|
||||||
|
registrarId
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
color="primary"
|
color="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
[disabled]="!unlockDomain.valid"
|
[disabled]="!unlockDomain.valid"
|
||||||
|
aria-label="Submit domain unlock request"
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
@@ -73,6 +74,7 @@
|
|||||||
color="primary"
|
color="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
[disabled]="!lockDomain.valid"
|
[disabled]="!lockDomain.valid"
|
||||||
|
aria-label="Submit domain lock request"
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { RegistryLockService } from './registryLock.service';
|
|||||||
selector: 'app-registry-lock',
|
selector: 'app-registry-lock',
|
||||||
templateUrl: './registryLock.component.html',
|
templateUrl: './registryLock.component.html',
|
||||||
styleUrls: ['./registryLock.component.scss'],
|
styleUrls: ['./registryLock.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class RegistryLockComponent {
|
export class RegistryLockComponent {
|
||||||
readonly isLocked = computed(() =>
|
readonly isLocked = computed(() =>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<mat-toolbar>
|
<mat-toolbar>
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
aria-label="Open menu"
|
aria-label="Open navigation menu"
|
||||||
(click)="toggleNavPane()"
|
(click)="toggleNavPane()"
|
||||||
*ngIf="breakpointObserver.isMobileView()"
|
*ngIf="breakpointObserver.isMobileView()"
|
||||||
class="console-app__menu-btn"
|
class="console-app__menu-btn"
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<a
|
<a
|
||||||
[routerLink]="'/home'"
|
[routerLink]="'/home'"
|
||||||
routerLinkActive="active"
|
routerLinkActive="active"
|
||||||
|
aria-label="Google Registry logo"
|
||||||
class="console-app__logo"
|
class="console-app__logo"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { HeaderComponent } from './header.component';
|
import { HeaderComponent } from './header.component';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { MaterialModule } from '../material.module';
|
import { MaterialModule } from '../material.module';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { AppModule, SelectedRegistrarModule } from '../app.module';
|
||||||
|
import { AppRoutingModule } from '../app-routing.module';
|
||||||
|
import { BackendService } from '../shared/services/backend.service';
|
||||||
|
import { provideHttpClient } from '@angular/common/http';
|
||||||
|
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||||
|
|
||||||
describe('HeaderComponent', () => {
|
describe('HeaderComponent', () => {
|
||||||
let component: HeaderComponent;
|
let component: HeaderComponent;
|
||||||
@@ -24,7 +30,19 @@ describe('HeaderComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [MaterialModule, BrowserAnimationsModule],
|
imports: [
|
||||||
|
SelectedRegistrarModule,
|
||||||
|
MaterialModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
AppRoutingModule,
|
||||||
|
AppModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
BackendService,
|
||||||
|
{ provide: ActivatedRoute, useValue: {} as ActivatedRoute },
|
||||||
|
provideHttpClient(),
|
||||||
|
provideHttpClientTesting(),
|
||||||
|
],
|
||||||
declarations: [HeaderComponent],
|
declarations: [HeaderComponent],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { BreakPointObserverService } from '../shared/services/breakPoint.service
|
|||||||
selector: 'app-header',
|
selector: 'app-header',
|
||||||
templateUrl: './header.component.html',
|
templateUrl: './header.component.html',
|
||||||
styleUrls: ['./header.component.scss'],
|
styleUrls: ['./header.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class HeaderComponent {
|
export class HeaderComponent {
|
||||||
private isNavOpen = false;
|
private isNavOpen = false;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
|
|
||||||
import { HomeComponent } from './home.component';
|
import { HomeComponent } from './home.component';
|
||||||
import { MaterialModule } from '../material.module';
|
import { MaterialModule } from '../material.module';
|
||||||
|
import { AppModule } from '../app.module';
|
||||||
|
|
||||||
describe('HomeComponent', () => {
|
describe('HomeComponent', () => {
|
||||||
let component: HomeComponent;
|
let component: HomeComponent;
|
||||||
@@ -23,7 +24,7 @@ describe('HomeComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [MaterialModule],
|
imports: [MaterialModule, AppModule],
|
||||||
declarations: [HomeComponent],
|
declarations: [HomeComponent],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { BreakPointObserverService } from '../shared/services/breakPoint.service
|
|||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
templateUrl: './home.component.html',
|
templateUrl: './home.component.html',
|
||||||
styleUrls: ['./home.component.scss'],
|
styleUrls: ['./home.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class HomeComponent {
|
export class HomeComponent {
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
}
|
}
|
||||||
&-subhead {
|
&-subhead {
|
||||||
font-size: 20px;
|
font-size: 1.25rem;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { DomainListComponent } from '../domains/domainList.component';
|
|||||||
templateUrl: './registryLockVerify.component.html',
|
templateUrl: './registryLockVerify.component.html',
|
||||||
styleUrls: ['./registryLockVerify.component.scss'],
|
styleUrls: ['./registryLockVerify.component.scss'],
|
||||||
providers: [RegistryLockVerifyService],
|
providers: [RegistryLockVerifyService],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class RegistryLockVerifyComponent {
|
export class RegistryLockVerifyComponent {
|
||||||
public static PATH = 'registry-lock-verify';
|
public static PATH = 'registry-lock-verify';
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
<mat-tree-node
|
<mat-tree-node
|
||||||
*matTreeNodeDef="let node"
|
*matTreeNodeDef="let node"
|
||||||
matTreeNodeToggle
|
matTreeNodeToggle
|
||||||
|
tabindex="0"
|
||||||
(click)="onClick(node)"
|
(click)="onClick(node)"
|
||||||
|
(keyup.enter)="onClick(node)"
|
||||||
[class.active]="router.url.includes(node.path)"
|
[class.active]="router.url.includes(node.path)"
|
||||||
[elementId]="getElementId(node)"
|
[elementId]="getElementId(node)"
|
||||||
>
|
>
|
||||||
@@ -18,6 +20,8 @@
|
|||||||
<mat-nested-tree-node
|
<mat-nested-tree-node
|
||||||
*matTreeNodeDef="let node; when: hasChild"
|
*matTreeNodeDef="let node; when: hasChild"
|
||||||
(click)="onClick(node)"
|
(click)="onClick(node)"
|
||||||
|
tabindex="0"
|
||||||
|
(keyup.enter)="onClick(node)"
|
||||||
>
|
>
|
||||||
<div class="mat-tree-node" [class.active]="router.url.includes(node.path)">
|
<div class="mat-tree-node" [class.active]="router.url.includes(node.path)">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ interface NavMenuNode extends RouteWithIcon {
|
|||||||
selector: 'app-navigation',
|
selector: 'app-navigation',
|
||||||
templateUrl: './navigation.component.html',
|
templateUrl: './navigation.component.html',
|
||||||
styleUrls: ['./navigation.component.scss'],
|
styleUrls: ['./navigation.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class NavigationComponent {
|
export class NavigationComponent {
|
||||||
renderRouter: boolean = true;
|
renderRouter: boolean = true;
|
||||||
|
|||||||
@@ -30,7 +30,14 @@
|
|||||||
>
|
>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</p>
|
</p>
|
||||||
<button mat-flat-button color="primary" type="submit">Save</button>
|
<button
|
||||||
|
mat-flat-button
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
aria-label="Submit new OT&E account"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export interface OteCreateResponse extends Map<string, string> {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-ote',
|
selector: 'app-ote',
|
||||||
standalone: true,
|
|
||||||
imports: [MaterialModule, SnackBarModule],
|
imports: [MaterialModule, SnackBarModule],
|
||||||
templateUrl: './newOte.component.html',
|
templateUrl: './newOte.component.html',
|
||||||
styleUrls: ['./newOte.component.scss'],
|
styleUrls: ['./newOte.component.scss'],
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ export interface OteStatusResponse {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-ote-status',
|
selector: 'app-ote-status',
|
||||||
standalone: true,
|
|
||||||
imports: [MaterialModule, SnackBarModule, CommonModule],
|
imports: [MaterialModule, SnackBarModule, CommonModule],
|
||||||
templateUrl: './oteStatus.component.html',
|
templateUrl: './oteStatus.component.html',
|
||||||
styleUrls: ['./oteStatus.component.scss'],
|
styleUrls: ['./oteStatus.component.scss'],
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ JPY=billing-id-for-yen"
|
|||||||
mat-flat-button
|
mat-flat-button
|
||||||
color="primary"
|
color="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
aria-label="Submit new registrar request"
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ interface LocalizedAddressStreet {
|
|||||||
templateUrl: './newRegistrar.component.html',
|
templateUrl: './newRegistrar.component.html',
|
||||||
styleUrls: ['./newRegistrar.component.scss'],
|
styleUrls: ['./newRegistrar.component.scss'],
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export default class NewRegistrarComponent {
|
export default class NewRegistrarComponent {
|
||||||
protected newRegistrar: Registrar;
|
protected newRegistrar: Registrar;
|
||||||
@@ -47,7 +48,6 @@ export default class NewRegistrarComponent {
|
|||||||
this.newRegistrar = {
|
this.newRegistrar = {
|
||||||
registrarId: '',
|
registrarId: '',
|
||||||
url: '',
|
url: '',
|
||||||
whoisServer: '',
|
|
||||||
registrarName: '',
|
registrarName: '',
|
||||||
icannReferralEmail: '',
|
icannReferralEmail: '',
|
||||||
localizedAddress: {
|
localizedAddress: {
|
||||||
|
|||||||
@@ -50,17 +50,16 @@ export interface SecuritySettings
|
|||||||
ipAddressAllowList?: Array<IpAllowListItem>;
|
ipAddressAllowList?: Array<IpAllowListItem>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WhoisRegistrarFields {
|
export interface RdapRegistrarFields {
|
||||||
ianaIdentifier?: number;
|
ianaIdentifier?: number;
|
||||||
icannReferralEmail: string;
|
icannReferralEmail: string;
|
||||||
localizedAddress: Address;
|
localizedAddress: Address;
|
||||||
registrarId: string;
|
registrarId: string;
|
||||||
url: string;
|
url: string;
|
||||||
whoisServer: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Registrar
|
export interface Registrar
|
||||||
extends WhoisRegistrarFields,
|
extends RdapRegistrarFields,
|
||||||
SecuritySettingsBackendModel {
|
SecuritySettingsBackendModel {
|
||||||
allowedTlds?: string[];
|
allowedTlds?: string[];
|
||||||
billingAccountMap?: object;
|
billingAccountMap?: object;
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
<div class="console-app__registrar-view">
|
<div
|
||||||
|
class="console-app__registrar-view"
|
||||||
|
cdkTrapFocus
|
||||||
|
[cdkTrapFocusAutoCapture]="true"
|
||||||
|
>
|
||||||
<h1 class="mat-headline-4">Registrars</h1>
|
<h1 class="mat-headline-4">Registrars</h1>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<div class="console-app__registrar-view-content">
|
<div class="console-app__registrar-view-content">
|
||||||
@@ -12,7 +16,7 @@
|
|||||||
*ngIf="oteButtonVisible"
|
*ngIf="oteButtonVisible"
|
||||||
mat-stroked-button
|
mat-stroked-button
|
||||||
(click)="checkOteStatus()"
|
(click)="checkOteStatus()"
|
||||||
aria-label="Check OT&E account"
|
aria-label="Check OT&E account status"
|
||||||
[elementId]="getElementIdForOteBlock()"
|
[elementId]="getElementIdForOteBlock()"
|
||||||
>
|
>
|
||||||
Check OT&E Status
|
Check OT&E Status
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { environment } from '../../environments/environment';
|
|||||||
selector: 'app-registrar-details',
|
selector: 'app-registrar-details',
|
||||||
templateUrl: './registrarDetails.component.html',
|
templateUrl: './registrarDetails.component.html',
|
||||||
styleUrls: ['./registrarDetails.component.scss'],
|
styleUrls: ['./registrarDetails.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class RegistrarDetailsComponent implements OnInit {
|
export class RegistrarDetailsComponent implements OnInit {
|
||||||
public static PATH = 'registrars/:id';
|
public static PATH = 'registrars/:id';
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
[ngModelOptions]="{ standalone: true }"
|
[ngModelOptions]="{ standalone: true }"
|
||||||
(focus)="onFocus()"
|
(focus)="onFocus()"
|
||||||
[matAutocomplete]="auto"
|
[matAutocomplete]="auto"
|
||||||
|
spellcheck="false"
|
||||||
/>
|
/>
|
||||||
<mat-autocomplete
|
<mat-autocomplete
|
||||||
autoActiveFirstOption
|
autoActiveFirstOption
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { RegistrarService } from './registrar.service';
|
|||||||
selector: 'app-registrar-selector',
|
selector: 'app-registrar-selector',
|
||||||
templateUrl: './registrarSelector.component.html',
|
templateUrl: './registrarSelector.component.html',
|
||||||
styleUrls: ['./registrarSelector.component.scss'],
|
styleUrls: ['./registrarSelector.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class RegistrarSelectorComponent {
|
export class RegistrarSelectorComponent {
|
||||||
registrarInput = signal<string>(this.registrarService.registrarId());
|
registrarInput = signal<string>(this.registrarService.registrarId());
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
} @else {
|
} @else {
|
||||||
<div class="console-app__registrars">
|
<div class="console-app__registrars">
|
||||||
<div class="console-app__registrars-header">
|
<div class="console-app__registrars-header">
|
||||||
<h1 class="mat-headline-4">Registrars</h1>
|
<h1 class="mat-headline-4" forceFocus>Registrars</h1>
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<button
|
<button
|
||||||
mat-stroked-button
|
mat-stroked-button
|
||||||
@@ -59,6 +59,8 @@
|
|||||||
<mat-row
|
<mat-row
|
||||||
*matRowDef="let row; columns: displayedColumns"
|
*matRowDef="let row; columns: displayedColumns"
|
||||||
(click)="openDetails(row.registrarId)"
|
(click)="openDetails(row.registrarId)"
|
||||||
|
tabindex="0"
|
||||||
|
(keyup.enter)="openDetails(row.registrarId)"
|
||||||
></mat-row>
|
></mat-row>
|
||||||
</mat-table>
|
</mat-table>
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export const columns = [
|
|||||||
templateUrl: './registrarsTable.component.html',
|
templateUrl: './registrarsTable.component.html',
|
||||||
styleUrls: ['./registrarsTable.component.scss'],
|
styleUrls: ['./registrarsTable.component.scss'],
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class RegistrarComponent {
|
export class RegistrarComponent {
|
||||||
public static PATH = 'registrars';
|
public static PATH = 'registrars';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<h1 class="mat-headline-4">Resources</h1>
|
<h1 class="mat-headline-4" forceFocus>Resources</h1>
|
||||||
<div class="console-app__resources">
|
<div class="console-app__resources">
|
||||||
<div>
|
<div>
|
||||||
<div class="console-app__resources-subhead">Technical resources</div>
|
<div class="console-app__resources-subhead">Technical resources</div>
|
||||||
@@ -11,6 +11,6 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<img src="./assets/resources.png" />
|
<img src="./assets/resources.png" alt="Generic resources image" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
&-subhead {
|
&-subhead {
|
||||||
font-size: 20px;
|
font-size: 1.25rem;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { UserDataService } from '../shared/services/userData.service';
|
|||||||
selector: 'app-resources',
|
selector: 'app-resources',
|
||||||
templateUrl: './resources.component.html',
|
templateUrl: './resources.component.html',
|
||||||
styleUrls: ['./resources.component.scss'],
|
styleUrls: ['./resources.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class ResourcesComponent {
|
export class ResourcesComponent {
|
||||||
public static PATH = 'resources';
|
public static PATH = 'resources';
|
||||||
|
|||||||
@@ -32,7 +32,9 @@
|
|||||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||||
<mat-row
|
<mat-row
|
||||||
*matRowDef="let row; columns: displayedColumns"
|
*matRowDef="let row; columns: displayedColumns"
|
||||||
|
tabindex="0"
|
||||||
(click)="openDetails(row)"
|
(click)="openDetails(row)"
|
||||||
|
(keyup.enter)="openDetails(row)"
|
||||||
></mat-row>
|
></mat-row>
|
||||||
</mat-table>
|
</mat-table>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
templateUrl: './contact.component.html',
|
templateUrl: './contact.component.html',
|
||||||
styleUrls: ['./contact.component.scss'],
|
styleUrls: ['./contact.component.scss'],
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export default class ContactComponent {
|
export default class ContactComponent {
|
||||||
public static PATH = 'contact';
|
public static PATH = 'contact';
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export type contactType =
|
|||||||
| 'LEGAL'
|
| 'LEGAL'
|
||||||
| 'MARKETING'
|
| 'MARKETING'
|
||||||
| 'TECH'
|
| 'TECH'
|
||||||
| 'WHOIS';
|
| 'RDAP';
|
||||||
|
|
||||||
type contactTypesToUserFriendlyTypes = { [type in contactType]: string };
|
type contactTypesToUserFriendlyTypes = { [type in contactType]: string };
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ export const contactTypeToTextMap: contactTypesToUserFriendlyTypes = {
|
|||||||
LEGAL: 'Legal contact',
|
LEGAL: 'Legal contact',
|
||||||
MARKETING: 'Marketing contact',
|
MARKETING: 'Marketing contact',
|
||||||
TECH: 'Technical contact',
|
TECH: 'Technical contact',
|
||||||
WHOIS: 'WHOIS-Inquiry contact',
|
RDAP: 'RDAP-Inquiry contact',
|
||||||
};
|
};
|
||||||
|
|
||||||
type UserFriendlyType = (typeof contactTypeToTextMap)[contactType];
|
type UserFriendlyType = (typeof contactTypeToTextMap)[contactType];
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
<div class="console-app__contact" *ngIf="contactService.contactInEdit">
|
<div
|
||||||
|
class="console-app__contact"
|
||||||
|
*ngIf="contactService.contactInEdit"
|
||||||
|
cdkTrapFocus
|
||||||
|
[cdkTrapFocusAutoCapture]="true"
|
||||||
|
>
|
||||||
<div class="console-app__contact-controls">
|
<div class="console-app__contact-controls">
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
@@ -92,12 +97,12 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h1>WHOIS Preferences</h1>
|
<h1>RDAP Preferences</h1>
|
||||||
<div>
|
<div>
|
||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
[(ngModel)]="contactService.contactInEdit.visibleInWhoisAsAdmin"
|
[(ngModel)]="contactService.contactInEdit.visibleInWhoisAsAdmin"
|
||||||
[ngModelOptions]="{ standalone: true }"
|
[ngModelOptions]="{ standalone: true }"
|
||||||
>Show in Registrar WHOIS record as admin contact</mat-checkbox
|
>Show in Registrar RDAP record as admin contact</mat-checkbox
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -105,7 +110,7 @@
|
|||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
[(ngModel)]="contactService.contactInEdit.visibleInWhoisAsTech"
|
[(ngModel)]="contactService.contactInEdit.visibleInWhoisAsTech"
|
||||||
[ngModelOptions]="{ standalone: true }"
|
[ngModelOptions]="{ standalone: true }"
|
||||||
>Show in Registrar WHOIS record as technical contact</mat-checkbox
|
>Show in Registrar RDAP record as technical contact</mat-checkbox
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -113,8 +118,8 @@
|
|||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
[(ngModel)]="contactService.contactInEdit.visibleInDomainWhoisAsAbuse"
|
[(ngModel)]="contactService.contactInEdit.visibleInDomainWhoisAsAbuse"
|
||||||
[ngModelOptions]="{ standalone: true }"
|
[ngModelOptions]="{ standalone: true }"
|
||||||
>Show Phone and Email in Domain WHOIS Record as registrar abuse
|
>Show Phone and Email in Domain RDAP Record as registrar abuse contact
|
||||||
contact (per CL&D requirements)</mat-checkbox
|
(per CL&D requirements)</mat-checkbox
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -123,6 +128,7 @@
|
|||||||
mat-flat-button
|
mat-flat-button
|
||||||
color="primary"
|
color="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
aria-label="Save contact updates"
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
@@ -170,13 +176,13 @@
|
|||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<mat-list role="list">
|
<mat-list role="list">
|
||||||
<mat-list-item role="listitem">
|
<mat-list-item role="listitem">
|
||||||
<h2>WHOIS Preferences</h2>
|
<h2>RDAP Preferences</h2>
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
@if(contactService.contactInEdit.visibleInWhoisAsAdmin) {
|
@if(contactService.contactInEdit.visibleInWhoisAsAdmin) {
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<mat-list-item role="listitem">
|
<mat-list-item role="listitem">
|
||||||
<span class="console-app__list-value"
|
<span class="console-app__list-value"
|
||||||
>Show in Registrar WHOIS record as admin contact</span
|
>Show in Registrar RDAP record as admin contact</span
|
||||||
>
|
>
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
} @if(contactService.contactInEdit.visibleInWhoisAsTech) {
|
} @if(contactService.contactInEdit.visibleInWhoisAsTech) {
|
||||||
@@ -186,14 +192,14 @@
|
|||||||
*ngIf="contactService.contactInEdit.visibleInWhoisAsTech"
|
*ngIf="contactService.contactInEdit.visibleInWhoisAsTech"
|
||||||
>
|
>
|
||||||
<span class="console-app__list-value"
|
<span class="console-app__list-value"
|
||||||
>Show in Registrar WHOIS record as technical contact</span
|
>Show in Registrar RDAP record as technical contact</span
|
||||||
>
|
>
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
} @if(contactService.contactInEdit.visibleInDomainWhoisAsAbuse) {
|
} @if(contactService.contactInEdit.visibleInDomainWhoisAsAbuse) {
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<mat-list-item role="listitem">
|
<mat-list-item role="listitem">
|
||||||
<span class="console-app__list-value"
|
<span class="console-app__list-value"
|
||||||
>Show Phone and Email in Domain WHOIS Record as registrar abuse
|
>Show Phone and Email in Domain RDAP Record as registrar abuse
|
||||||
contact (per CL&D requirements)</span
|
contact (per CL&D requirements)</span
|
||||||
>
|
>
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
selector: 'app-contact-details',
|
selector: 'app-contact-details',
|
||||||
templateUrl: './contactDetails.component.html',
|
templateUrl: './contactDetails.component.html',
|
||||||
styleUrls: ['./contactDetails.component.scss'],
|
styleUrls: ['./contactDetails.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class ContactDetailsComponent {
|
export class ContactDetailsComponent {
|
||||||
protected contactTypeToTextMap = contactTypeToTextMap;
|
protected contactTypeToTextMap = contactTypeToTextMap;
|
||||||
|
|||||||
+8
-48
@@ -1,18 +1,18 @@
|
|||||||
@if(whoisService.editing) {
|
@if(rdapService.editing) {
|
||||||
<app-whois-edit></app-whois-edit>
|
<app-rdap-edit></app-rdap-edit>
|
||||||
} @else {
|
} @else {
|
||||||
<div class="console-app__whois">
|
<div class="console-app__rdap">
|
||||||
<div class="console-app__whois-controls">
|
<div class="console-app__rdap-controls">
|
||||||
<span>
|
<span>
|
||||||
General registrar information for your WHOIS record. This information is
|
General registrar information for your RDAP record. This information is
|
||||||
always visible in WHOIS.
|
always visible in RDAP.
|
||||||
</span>
|
</span>
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<button
|
<button
|
||||||
mat-flat-button
|
mat-flat-button
|
||||||
color="primary"
|
color="primary"
|
||||||
aria-label="Edit WHOIS record"
|
aria-label="Edit RDAP record"
|
||||||
(click)="whoisService.editing = true"
|
(click)="rdapService.editing = true"
|
||||||
>
|
>
|
||||||
<mat-icon>edit</mat-icon>
|
<mat-icon>edit</mat-icon>
|
||||||
Edit
|
Edit
|
||||||
@@ -61,45 +61,5 @@
|
|||||||
</mat-list>
|
</mat-list>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<mat-card appearance="outlined">
|
|
||||||
<mat-card-content>
|
|
||||||
<mat-list role="list">
|
|
||||||
<mat-list-item role="listitem">
|
|
||||||
<h2>Technical Info</h2>
|
|
||||||
</mat-list-item>
|
|
||||||
<mat-divider></mat-divider>
|
|
||||||
<mat-list-item role="listitem">
|
|
||||||
<span class="console-app__list-key">IANA Identifier</span>
|
|
||||||
<span class="console-app__list-value">{{
|
|
||||||
registrarService.registrar()?.ianaIdentifier
|
|
||||||
}}</span>
|
|
||||||
</mat-list-item>
|
|
||||||
<mat-divider></mat-divider>
|
|
||||||
<mat-list-item role="listitem">
|
|
||||||
<div>
|
|
||||||
<span class="console-app__list-key">ICANN Referral Email</span>
|
|
||||||
<span class="console-app__list-value">{{
|
|
||||||
registrarService.registrar()?.icannReferralEmail
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
</mat-list-item>
|
|
||||||
<mat-divider></mat-divider>
|
|
||||||
<mat-list-item role="listitem">
|
|
||||||
<span class="console-app__list-key">WHOIS server</span>
|
|
||||||
<span class="console-app__list-value">{{
|
|
||||||
registrarService.registrar()?.whoisServer
|
|
||||||
}}</span>
|
|
||||||
</mat-list-item>
|
|
||||||
<mat-divider></mat-divider>
|
|
||||||
<mat-list-item role="listitem">
|
|
||||||
<span class="console-app__list-key">Referral URL</span>
|
|
||||||
<span class="console-app__list-value">{{
|
|
||||||
registrarService.registrar()?.url
|
|
||||||
}}</span>
|
|
||||||
</mat-list-item>
|
|
||||||
</mat-list>
|
|
||||||
</mat-card-content>
|
|
||||||
</mat-card>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
.console-app__whois {
|
.console-app__rdap {
|
||||||
max-width: 616px;
|
max-width: 616px;
|
||||||
|
|
||||||
&-controls {
|
&-controls {
|
||||||
+6
-6
@@ -20,15 +20,15 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|||||||
import { MaterialModule } from 'src/app/material.module';
|
import { MaterialModule } from 'src/app/material.module';
|
||||||
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
||||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||||
import WhoisComponent from './whois.component';
|
import RdapComponent from './rdap.component';
|
||||||
|
|
||||||
describe('WhoisComponent', () => {
|
describe('RdapComponent', () => {
|
||||||
let component: WhoisComponent;
|
let component: RdapComponent;
|
||||||
let fixture: ComponentFixture<WhoisComponent>;
|
let fixture: ComponentFixture<RdapComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [WhoisComponent],
|
declarations: [RdapComponent],
|
||||||
imports: [MaterialModule, BrowserAnimationsModule],
|
imports: [MaterialModule, BrowserAnimationsModule],
|
||||||
providers: [
|
providers: [
|
||||||
BackendService,
|
BackendService,
|
||||||
@@ -45,7 +45,7 @@ describe('WhoisComponent', () => {
|
|||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(WhoisComponent);
|
fixture = TestBed.createComponent(RdapComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
+8
-8
@@ -14,16 +14,16 @@
|
|||||||
|
|
||||||
import { Component, computed } from '@angular/core';
|
import { Component, computed } from '@angular/core';
|
||||||
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
||||||
|
import { RdapService } from './rdap.service';
|
||||||
import { WhoisService } from './whois.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-whois',
|
selector: 'app-rdap',
|
||||||
templateUrl: './whois.component.html',
|
templateUrl: './rdap.component.html',
|
||||||
styleUrls: ['./whois.component.scss'],
|
styleUrls: ['./rdap.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export default class WhoisComponent {
|
export default class RdapComponent {
|
||||||
public static PATH = 'whois';
|
public static PATH = 'rdap';
|
||||||
formattedAddress = computed(() => {
|
formattedAddress = computed(() => {
|
||||||
let result = '';
|
let result = '';
|
||||||
const registrar = this.registrarService.registrar();
|
const registrar = this.registrarService.registrar();
|
||||||
@@ -46,7 +46,7 @@ export default class WhoisComponent {
|
|||||||
});
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public whoisService: WhoisService,
|
public rdapService: RdapService,
|
||||||
public registrarService: RegistrarService
|
public registrarService: RegistrarService
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
+4
-4
@@ -16,14 +16,14 @@ import { Injectable } from '@angular/core';
|
|||||||
import { switchMap } from 'rxjs';
|
import { switchMap } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
RegistrarService,
|
RegistrarService,
|
||||||
WhoisRegistrarFields,
|
RdapRegistrarFields,
|
||||||
} from 'src/app/registrar/registrar.service';
|
} from 'src/app/registrar/registrar.service';
|
||||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class WhoisService {
|
export class RdapService {
|
||||||
editing: boolean = false;
|
editing: boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -31,8 +31,8 @@ export class WhoisService {
|
|||||||
private registrarService: RegistrarService
|
private registrarService: RegistrarService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
saveChanges(newWhoisRegistrarFields: WhoisRegistrarFields) {
|
saveChanges(newRdapRegistrarFields: RdapRegistrarFields) {
|
||||||
return this.backend.postWhoisRegistrarFields(newWhoisRegistrarFields).pipe(
|
return this.backend.postRdapRegistrarFields(newRdapRegistrarFields).pipe(
|
||||||
switchMap(() => {
|
switchMap(() => {
|
||||||
return this.registrarService.loadRegistrars();
|
return this.registrarService.loadRegistrars();
|
||||||
})
|
})
|
||||||
+21
-43
@@ -1,22 +1,27 @@
|
|||||||
<div class="console-app__whois-edit" *ngIf="registrarInEdit">
|
<div
|
||||||
|
class="console-app__rdap-edit"
|
||||||
|
*ngIf="registrarInEdit"
|
||||||
|
cdkTrapFocus
|
||||||
|
[cdkTrapFocusAutoCapture]="true"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
class="console-app__whois-edit-back"
|
class="console-app__rdap-edit-back"
|
||||||
aria-label="Back to whois view"
|
aria-label="Back to rdap view"
|
||||||
(click)="whoisService.editing = false"
|
(click)="rdapService.editing = false"
|
||||||
>
|
>
|
||||||
<mat-icon>arrow_back</mat-icon>
|
<mat-icon>arrow_back</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="console-app__whois-edit-controls">
|
<div class="console-app__rdap-edit-controls">
|
||||||
<span>
|
<span>
|
||||||
General registrar information for your WHOIS record. This information is
|
General registrar information for your RDAP record. This information is
|
||||||
always visible in WHOIS.
|
always visible in RDAP.
|
||||||
</span>
|
</span>
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="console-app__whois-edit">
|
<div class="console-app__rdap-edit">
|
||||||
<h1>Personal info</h1>
|
<h1>Personal info</h1>
|
||||||
|
|
||||||
<form (ngSubmit)="save($event)">
|
<form (ngSubmit)="save($event)">
|
||||||
@@ -110,41 +115,14 @@
|
|||||||
/>
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<h1>Technical info</h1>
|
<button
|
||||||
|
mat-flat-button
|
||||||
<mat-form-field appearance="outline">
|
color="primary"
|
||||||
<mat-label>WHOIS server: </mat-label>
|
type="submit"
|
||||||
<input
|
aria-label="Save RDAO settings"
|
||||||
matInput
|
>
|
||||||
type="text"
|
Save
|
||||||
[(ngModel)]="registrarInEdit.whoisServer"
|
</button>
|
||||||
[ngModelOptions]="{ standalone: true }"
|
|
||||||
/>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
|
||||||
<mat-label>Referral URL: </mat-label>
|
|
||||||
<input
|
|
||||||
matInput
|
|
||||||
type="text"
|
|
||||||
[(ngModel)]="registrarInEdit.url"
|
|
||||||
[ngModelOptions]="{ standalone: true }"
|
|
||||||
/>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
@if((userDataService.userData()?.globalRole || 'NONE') !== "NONE") {
|
|
||||||
<mat-form-field appearance="outline">
|
|
||||||
<mat-label>ICANN Referral Email: </mat-label>
|
|
||||||
<input
|
|
||||||
matInput
|
|
||||||
type="text"
|
|
||||||
[(ngModel)]="registrarInEdit.icannReferralEmail"
|
|
||||||
[ngModelOptions]="{ standalone: true }"
|
|
||||||
/>
|
|
||||||
</mat-form-field>
|
|
||||||
}
|
|
||||||
|
|
||||||
<button mat-flat-button color="primary" type="submit">Save</button>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
.console-app__whois-edit {
|
.console-app__rdap-edit {
|
||||||
max-width: 616px;
|
max-width: 616px;
|
||||||
|
|
||||||
&-controls {
|
&-controls {
|
||||||
+9
-8
@@ -20,19 +20,20 @@ import {
|
|||||||
RegistrarService,
|
RegistrarService,
|
||||||
} from 'src/app/registrar/registrar.service';
|
} from 'src/app/registrar/registrar.service';
|
||||||
import { UserDataService } from 'src/app/shared/services/userData.service';
|
import { UserDataService } from 'src/app/shared/services/userData.service';
|
||||||
import { WhoisService } from './whois.service';
|
import { RdapService } from './rdap.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-whois-edit',
|
selector: 'app-rdap-edit',
|
||||||
templateUrl: './whoisEdit.component.html',
|
templateUrl: './rdapEdit.component.html',
|
||||||
styleUrls: ['./whoisEdit.component.scss'],
|
styleUrls: ['./rdapEdit.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export default class WhoisEditComponent {
|
export default class RdapEditComponent {
|
||||||
registrarInEdit: Registrar | undefined;
|
registrarInEdit: Registrar | undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public userDataService: UserDataService,
|
public userDataService: UserDataService,
|
||||||
public whoisService: WhoisService,
|
public rdapService: RdapService,
|
||||||
public registrarService: RegistrarService,
|
public registrarService: RegistrarService,
|
||||||
private _snackBar: MatSnackBar
|
private _snackBar: MatSnackBar
|
||||||
) {
|
) {
|
||||||
@@ -48,9 +49,9 @@ export default class WhoisEditComponent {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!this.registrarInEdit) return;
|
if (!this.registrarInEdit) return;
|
||||||
|
|
||||||
this.whoisService.saveChanges(this.registrarInEdit).subscribe({
|
this.rdapService.saveChanges(this.registrarInEdit).subscribe({
|
||||||
complete: () => {
|
complete: () => {
|
||||||
this.whoisService.editing = false;
|
this.rdapService.editing = false;
|
||||||
},
|
},
|
||||||
error: (err: HttpErrorResponse) => {
|
error: (err: HttpErrorResponse) => {
|
||||||
this._snackBar.open(err.error);
|
this._snackBar.open(err.error);
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
<div class="settings-security__edit-password">
|
<div
|
||||||
|
class="settings-security__edit-password"
|
||||||
|
cdkTrapFocus
|
||||||
|
[cdkTrapFocusAutoCapture]="true"
|
||||||
|
>
|
||||||
<p>
|
<p>
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ type errorFriendlyText = { [type in errorCode]: String };
|
|||||||
selector: 'app-epp-password-edit',
|
selector: 'app-epp-password-edit',
|
||||||
templateUrl: './eppPasswordEdit.component.html',
|
templateUrl: './eppPasswordEdit.component.html',
|
||||||
styleUrls: ['./eppPasswordEdit.component.scss'],
|
styleUrls: ['./eppPasswordEdit.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export default class EppPasswordEditComponent {
|
export default class EppPasswordEditComponent {
|
||||||
MIN_MAX_LENGHT = new String(
|
MIN_MAX_LENGHT = new String(
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ describe('SecurityComponent', () => {
|
|||||||
fetchSecurityDetailsSpy =
|
fetchSecurityDetailsSpy =
|
||||||
securityServiceSpy.fetchSecurityDetails.and.returnValue(of());
|
securityServiceSpy.fetchSecurityDetails.and.returnValue(of());
|
||||||
|
|
||||||
saveSpy = securityServiceSpy.saveChanges;
|
saveSpy = securityServiceSpy.saveChanges.and.returnValue(of());
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [SecurityEditComponent, SecurityComponent],
|
declarations: [SecurityEditComponent, SecurityComponent],
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { SecurityService, apiToUiConverter } from './security.service';
|
|||||||
selector: 'app-security',
|
selector: 'app-security',
|
||||||
templateUrl: './security.component.html',
|
templateUrl: './security.component.html',
|
||||||
styleUrls: ['./security.component.scss'],
|
styleUrls: ['./security.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export default class SecurityComponent {
|
export default class SecurityComponent {
|
||||||
public static PATH = 'security';
|
public static PATH = 'security';
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { switchMap, timeout } from 'rxjs';
|
import { switchMap } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
IpAllowListItem,
|
IpAllowListItem,
|
||||||
RegistrarService,
|
RegistrarService,
|
||||||
@@ -69,7 +69,6 @@ export class SecurityService {
|
|||||||
uiToApiConverter(newSecuritySettings)
|
uiToApiConverter(newSecuritySettings)
|
||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
timeout(2000),
|
|
||||||
switchMap(() => {
|
switchMap(() => {
|
||||||
return this.registrarService.loadRegistrars();
|
return this.registrarService.loadRegistrars();
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
<div class="settings-security__edit">
|
<div
|
||||||
|
class="settings-security__edit"
|
||||||
|
cdkTrapFocus
|
||||||
|
[cdkTrapFocusAutoCapture]="true"
|
||||||
|
>
|
||||||
<h1>IP Allowlist</h1>
|
<h1>IP Allowlist</h1>
|
||||||
<p>
|
<p>
|
||||||
Restrict access to EPP production servers to the following IP/IPv6
|
Restrict access to EPP production servers to the following IP/IPv6
|
||||||
@@ -19,7 +23,7 @@
|
|||||||
<button
|
<button
|
||||||
matSuffix
|
matSuffix
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
aria-label="Remove"
|
[attr.aria-label]="'Remove IP entry ' + ip.value"
|
||||||
(click)="removeIpEntry(ip)"
|
(click)="removeIpEntry(ip)"
|
||||||
[disabled]="isUpdating"
|
[disabled]="isUpdating"
|
||||||
>
|
>
|
||||||
@@ -32,6 +36,7 @@
|
|||||||
[disabled]="isUpdating"
|
[disabled]="isUpdating"
|
||||||
color="primary"
|
color="primary"
|
||||||
(click)="createIpEntry()"
|
(click)="createIpEntry()"
|
||||||
|
aria-label="Add new IP address"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
+ Add IP
|
+ Add IP
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { SecurityService, apiToUiConverter } from './security.service';
|
|||||||
selector: 'app-security-edit',
|
selector: 'app-security-edit',
|
||||||
templateUrl: './securityEdit.component.html',
|
templateUrl: './securityEdit.component.html',
|
||||||
styleUrls: ['./securityEdit.component.scss'],
|
styleUrls: ['./securityEdit.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export default class SecurityEditComponent {
|
export default class SecurityEditComponent {
|
||||||
dataSource: SecuritySettings = {};
|
dataSource: SecuritySettings = {};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<app-selected-registrar-wrapper>
|
<app-selected-registrar-wrapper>
|
||||||
<div class="console-settings">
|
<div class="console-settings">
|
||||||
<h1 class="mat-headline-4">Settings</h1>
|
<h1 class="mat-headline-4" forceFocus>Settings</h1>
|
||||||
<nav
|
<nav
|
||||||
mat-tab-nav-bar
|
mat-tab-nav-bar
|
||||||
mat-stretch-tabs="false"
|
mat-stretch-tabs="false"
|
||||||
@@ -10,22 +10,31 @@
|
|||||||
<a
|
<a
|
||||||
mat-tab-link
|
mat-tab-link
|
||||||
routerLink="contact"
|
routerLink="contact"
|
||||||
routerLinkActive="active-link"
|
routerLinkActive
|
||||||
queryParamsHandling="merge"
|
queryParamsHandling="merge"
|
||||||
|
#rla="routerLinkActive"
|
||||||
|
[active]="rla.isActive"
|
||||||
|
aria-label="Access contacts settings"
|
||||||
>Contacts</a
|
>Contacts</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
mat-tab-link
|
mat-tab-link
|
||||||
routerLink="whois"
|
routerLink="rdap"
|
||||||
routerLinkActive="active-link"
|
routerLinkActive
|
||||||
queryParamsHandling="merge"
|
queryParamsHandling="merge"
|
||||||
>WHOIS Info</a
|
#rla2="routerLinkActive"
|
||||||
|
[active]="rla2.isActive"
|
||||||
|
aria-label="Access rdap settings"
|
||||||
|
>RDAP Info</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
mat-tab-link
|
mat-tab-link
|
||||||
routerLink="security"
|
routerLink="security"
|
||||||
routerLinkActive="active-link"
|
routerLinkActive
|
||||||
queryParamsHandling="merge"
|
queryParamsHandling="merge"
|
||||||
|
#rla3="routerLinkActive"
|
||||||
|
[active]="rla3.isActive"
|
||||||
|
aria-label="Access security settings"
|
||||||
>Security</a
|
>Security</a
|
||||||
>
|
>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -13,14 +13,6 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
.console-settings {
|
.console-settings {
|
||||||
.mdc-tab {
|
|
||||||
&.active-link {
|
|
||||||
border-bottom: 2px solid var(--primary);
|
|
||||||
.mdc-tab__text-label {
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nav {
|
nav {
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { SettingsComponent } from './settings.component';
|
import { SettingsComponent } from './settings.component';
|
||||||
import { MaterialModule } from '../material.module';
|
import { MaterialModule } from '../material.module';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { AppModule, SelectedRegistrarModule } from '../app.module';
|
||||||
|
import { BackendService } from '../shared/services/backend.service';
|
||||||
|
import { provideHttpClient } from '@angular/common/http';
|
||||||
|
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||||
|
import { AppRoutingModule } from '../app-routing.module';
|
||||||
|
|
||||||
describe('SettingsComponent', () => {
|
describe('SettingsComponent', () => {
|
||||||
let component: SettingsComponent;
|
let component: SettingsComponent;
|
||||||
@@ -24,7 +30,19 @@ describe('SettingsComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [MaterialModule, BrowserAnimationsModule],
|
imports: [
|
||||||
|
SelectedRegistrarModule,
|
||||||
|
MaterialModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
AppRoutingModule,
|
||||||
|
AppModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
BackendService,
|
||||||
|
{ provide: ActivatedRoute, useValue: {} as ActivatedRoute },
|
||||||
|
provideHttpClient(),
|
||||||
|
provideHttpClientTesting(),
|
||||||
|
],
|
||||||
declarations: [SettingsComponent],
|
declarations: [SettingsComponent],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { Component, ViewEncapsulation } from '@angular/core';
|
|||||||
templateUrl: './settings.component.html',
|
templateUrl: './settings.component.html',
|
||||||
styleUrls: ['./settings.component.scss'],
|
styleUrls: ['./settings.component.scss'],
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class SettingsComponent {
|
export class SettingsComponent {
|
||||||
public static PATH = 'settings';
|
public static PATH = 'settings';
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ interface Notification {
|
|||||||
selector: 'app-notifications',
|
selector: 'app-notifications',
|
||||||
templateUrl: './notifications.component.html',
|
templateUrl: './notifications.component.html',
|
||||||
styleUrls: ['./notifications.component.scss'],
|
styleUrls: ['./notifications.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class NotificationsComponent {
|
export class NotificationsComponent {
|
||||||
protected mockNotifications: Notification[] = [
|
protected mockNotifications: Notification[] = [
|
||||||
|
|||||||
+1
@@ -31,6 +31,7 @@ import { RegistrarService } from 'src/app/registrar/registrar.service';
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class SelectedRegistrarWrapper {
|
export class SelectedRegistrarWrapper {
|
||||||
constructor(protected registrarService: RegistrarService) {}
|
constructor(protected registrarService: RegistrarService) {}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Directive, ElementRef, effect } from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[forceFocus]',
|
||||||
|
standalone: false,
|
||||||
|
})
|
||||||
|
export class ForceFocusDirective {
|
||||||
|
constructor(private el: ElementRef) {
|
||||||
|
effect(this.processElement.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
processElement() {
|
||||||
|
this.el.nativeElement.tabIndex = '1';
|
||||||
|
this.el.nativeElement.focus();
|
||||||
|
this.el.nativeElement.tabIndex = '-1';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ import { Directive, HostListener } from '@angular/core';
|
|||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[backButton]',
|
selector: '[backButton]',
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class LocationBackDirective {
|
export class LocationBackDirective {
|
||||||
constructor(private location: Location) {}
|
constructor(private location: Location) {}
|
||||||
|
|||||||
@@ -19,20 +19,23 @@ export enum RESTRICTED_ELEMENTS {
|
|||||||
REGISTRAR_ELEMENT,
|
REGISTRAR_ELEMENT,
|
||||||
OTE,
|
OTE,
|
||||||
USERS,
|
USERS,
|
||||||
|
BULK_DELETE,
|
||||||
|
SUSPEND,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DISABLED_ELEMENTS_PER_ROLE = {
|
export const DISABLED_ELEMENTS_PER_ROLE = {
|
||||||
NONE: [
|
NONE: [
|
||||||
RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT,
|
RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT,
|
||||||
RESTRICTED_ELEMENTS.OTE,
|
RESTRICTED_ELEMENTS.OTE,
|
||||||
RESTRICTED_ELEMENTS.USERS,
|
RESTRICTED_ELEMENTS.SUSPEND,
|
||||||
],
|
],
|
||||||
SUPPORT_LEAD: [RESTRICTED_ELEMENTS.USERS],
|
SUPPORT_LEAD: [],
|
||||||
SUPPORT_AGENT: [RESTRICTED_ELEMENTS.USERS],
|
SUPPORT_AGENT: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[elementId]',
|
selector: '[elementId]',
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class UserLevelVisibility {
|
export class UserLevelVisibility {
|
||||||
@Input() elementId!: RESTRICTED_ELEMENTS | null;
|
@Input() elementId!: RESTRICTED_ELEMENTS | null;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { User } from 'src/app/users/users.service';
|
|||||||
import {
|
import {
|
||||||
Registrar,
|
Registrar,
|
||||||
SecuritySettingsBackendModel,
|
SecuritySettingsBackendModel,
|
||||||
WhoisRegistrarFields,
|
RdapRegistrarFields,
|
||||||
} from '../../registrar/registrar.service';
|
} from '../../registrar/registrar.service';
|
||||||
import { Contact } from '../../settings/contact/contact.service';
|
import { Contact } from '../../settings/contact/contact.service';
|
||||||
import { EppPasswordBackendModel } from '../../settings/security/security.service';
|
import { EppPasswordBackendModel } from '../../settings/security/security.service';
|
||||||
@@ -180,6 +180,23 @@ export class BackendService {
|
|||||||
.pipe(catchError((err) => this.errorCatcher<any>(err)));
|
.pipe(catchError((err) => this.errorCatcher<any>(err)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bulkDomainAction(
|
||||||
|
domainNames: string[],
|
||||||
|
reason: string,
|
||||||
|
bulkDomainAction: string,
|
||||||
|
registrarId: string
|
||||||
|
) {
|
||||||
|
return this.http
|
||||||
|
.post<any>(
|
||||||
|
`/console-api/bulk-domain?registrarId=${registrarId}&bulkDomainAction=${bulkDomainAction}`,
|
||||||
|
{
|
||||||
|
domainList: domainNames,
|
||||||
|
reason,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.pipe(catchError((err) => this.errorCatcher<any>(err)));
|
||||||
|
}
|
||||||
|
|
||||||
updateUser(registrarId: string, updatedUser: User): Observable<any> {
|
updateUser(registrarId: string, updatedUser: User): Observable<any> {
|
||||||
return this.http
|
return this.http
|
||||||
.put<User>(`/console-api/users?registrarId=${registrarId}`, updatedUser)
|
.put<User>(`/console-api/users?registrarId=${registrarId}`, updatedUser)
|
||||||
@@ -192,12 +209,12 @@ export class BackendService {
|
|||||||
.pipe(catchError((err) => this.errorCatcher<UserData>(err)));
|
.pipe(catchError((err) => this.errorCatcher<UserData>(err)));
|
||||||
}
|
}
|
||||||
|
|
||||||
postWhoisRegistrarFields(
|
postRdapRegistrarFields(
|
||||||
whoisRegistrarFields: WhoisRegistrarFields
|
rdapRegistrarFields: RdapRegistrarFields
|
||||||
): Observable<WhoisRegistrarFields> {
|
): Observable<RdapRegistrarFields> {
|
||||||
return this.http.post<WhoisRegistrarFields>(
|
return this.http.post<RdapRegistrarFields>(
|
||||||
'/console-api/settings/whois-fields',
|
'/console-api/settings/rdap-fields',
|
||||||
whoisRegistrarFields
|
rdapRegistrarFields
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,10 @@
|
|||||||
For help with OT&E sandbox and certification, or new technical requirements
|
For help with OT&E sandbox and certification, or new technical requirements
|
||||||
for any of our new TLD launches.
|
for any of our new TLD launches.
|
||||||
</p>
|
</p>
|
||||||
<a class="text-l" href="mailto:registry-integration@google.com"
|
<a
|
||||||
|
class="text-l"
|
||||||
|
href="mailto:registry-integration@google.com"
|
||||||
|
aria-label="Email us with OT&E sandbox/certification or new TLD technical requirements questions."
|
||||||
>registry-integration@google.com</a
|
>registry-integration@google.com</a
|
||||||
>
|
>
|
||||||
<p class="text-l">
|
<p class="text-l">
|
||||||
@@ -19,6 +22,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
class="text-l"
|
class="text-l"
|
||||||
|
aria-label="Email support with general purpose questions."
|
||||||
href="mailto:{{ userDataService.userData()?.supportEmail }}"
|
href="mailto:{{ userDataService.userData()?.supportEmail }}"
|
||||||
>{{ userDataService.userData()?.supportEmail }}</a
|
>{{ userDataService.userData()?.supportEmail }}</a
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { UserDataService } from '../shared/services/userData.service';
|
|||||||
selector: 'app-support',
|
selector: 'app-support',
|
||||||
templateUrl: './support.component.html',
|
templateUrl: './support.component.html',
|
||||||
styleUrls: ['./support.component.scss'],
|
styleUrls: ['./support.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class SupportComponent {
|
export class SupportComponent {
|
||||||
public static PATH = 'support';
|
public static PATH = 'support';
|
||||||
|
|||||||
@@ -18,5 +18,6 @@ import { Component } from '@angular/core';
|
|||||||
selector: 'app-tlds',
|
selector: 'app-tlds',
|
||||||
templateUrl: './tlds.component.html',
|
templateUrl: './tlds.component.html',
|
||||||
styleUrls: ['./tlds.component.scss'],
|
styleUrls: ['./tlds.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class TldsComponent {}
|
export class TldsComponent {}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
<div class="console-app__user-details">
|
<div
|
||||||
|
class="console-app__user-details"
|
||||||
|
cdkTrapFocus
|
||||||
|
[cdkTrapFocusAutoCapture]="true"
|
||||||
|
>
|
||||||
@if(isEditing) {
|
@if(isEditing) {
|
||||||
<h1 class="mat-headline-4">Editing {{ userDetails().emailAddress }}</h1>
|
<h1 class="mat-headline-4">Editing {{ userDetails().emailAddress }}</h1>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
@@ -86,11 +90,12 @@
|
|||||||
<input
|
<input
|
||||||
[type]="isPasswordVisible ? 'text' : 'password'"
|
[type]="isPasswordVisible ? 'text' : 'password'"
|
||||||
[value]="userDetails().password"
|
[value]="userDetails().password"
|
||||||
disabled
|
aria-label="Password field"
|
||||||
|
readonly
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
mat-button
|
mat-button
|
||||||
aria-label="Show password"
|
aria-hidden="true"
|
||||||
(click)="isPasswordVisible = !isPasswordVisible"
|
(click)="isPasswordVisible = !isPasswordVisible"
|
||||||
>
|
>
|
||||||
{{ isPasswordVisible ? "Hide" : "View" }} password
|
{{ isPasswordVisible ? "Hide" : "View" }} password
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
.console-app {
|
.console-app {
|
||||||
&__user-details {
|
&__user-details {
|
||||||
|
max-width: 616px;
|
||||||
&-controls {
|
&-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -34,6 +35,5 @@
|
|||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
max-width: 616px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import { UserEditFormComponent } from './userEditForm.component';
|
|||||||
selector: 'app-user-edit',
|
selector: 'app-user-edit',
|
||||||
templateUrl: './userDetails.component.html',
|
templateUrl: './userDetails.component.html',
|
||||||
styleUrls: ['./userDetails.component.scss'],
|
styleUrls: ['./userDetails.component.scss'],
|
||||||
standalone: true,
|
|
||||||
imports: [
|
imports: [
|
||||||
FormsModule,
|
FormsModule,
|
||||||
MaterialModule,
|
MaterialModule,
|
||||||
@@ -68,17 +67,24 @@ export class UserDetailsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deleteUser() {
|
deleteUser() {
|
||||||
this.isLoading = true;
|
if (
|
||||||
this.usersService.deleteUser(this.userDetails()).subscribe({
|
confirm(
|
||||||
error: (err) => {
|
'This will permanently delete the user ' +
|
||||||
this._snackBar.open(err.error || err.message);
|
this.userDetails().emailAddress
|
||||||
this.isLoading = false;
|
)
|
||||||
},
|
) {
|
||||||
complete: () => {
|
this.isLoading = true;
|
||||||
this.isLoading = false;
|
this.usersService.deleteUser(this.userDetails()).subscribe({
|
||||||
this.goBack();
|
error: (err) => {
|
||||||
},
|
this._snackBar.open(err.error || err.message);
|
||||||
});
|
this.isLoading = false;
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.goBack();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
goBack() {
|
goBack() {
|
||||||
|
|||||||
@@ -33,7 +33,13 @@
|
|||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</p>
|
</p>
|
||||||
<button mat-flat-button color="primary" aria-label="Save user" type="submit">
|
<button
|
||||||
|
mat-flat-button
|
||||||
|
color="primary"
|
||||||
|
aria-label="Save user"
|
||||||
|
type="submit"
|
||||||
|
aria-label="Save changes to the user"
|
||||||
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -29,19 +29,17 @@ import { User } from './users.service';
|
|||||||
selector: 'app-user-edit-form',
|
selector: 'app-user-edit-form',
|
||||||
templateUrl: './userEditForm.component.html',
|
templateUrl: './userEditForm.component.html',
|
||||||
styleUrls: ['./userEditForm.component.scss'],
|
styleUrls: ['./userEditForm.component.scss'],
|
||||||
standalone: true,
|
|
||||||
imports: [FormsModule, MaterialModule, CommonModule],
|
imports: [FormsModule, MaterialModule, CommonModule],
|
||||||
providers: [],
|
providers: [],
|
||||||
})
|
})
|
||||||
export class UserEditFormComponent {
|
export class UserEditFormComponent {
|
||||||
@ViewChild('form') form!: ElementRef;
|
@ViewChild('form') form!: ElementRef;
|
||||||
isNew = input<boolean>(false);
|
isNew = input<boolean>(false);
|
||||||
user = input<User>(
|
user = input<User, User>(
|
||||||
{
|
{
|
||||||
emailAddress: '',
|
emailAddress: '',
|
||||||
role: 'ACCOUNT_MANAGER',
|
role: 'ACCOUNT_MANAGER',
|
||||||
},
|
},
|
||||||
// @ts-ignore - legit option, typescript fails to match it to a proper type
|
|
||||||
{ transform: (user: User) => structuredClone(user) }
|
{ transform: (user: User) => structuredClone(user) }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<mat-spinner />
|
<mat-spinner />
|
||||||
</div>
|
</div>
|
||||||
} @else if(selectingExistingUser) {
|
} @else if(selectingExistingUser) {
|
||||||
<div class="console-app__users">
|
<div class="console-app__users" cdkTrapFocus [cdkTrapFocusAutoCapture]="true">
|
||||||
<h1 class="mat-headline-4">Add existing user</h1>
|
<h1 class="mat-headline-4">Add existing user</h1>
|
||||||
<p>
|
<p>
|
||||||
<button
|
<button
|
||||||
@@ -58,24 +58,32 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
} @else if(usersService.currentlyOpenUserEmail()) {
|
} @else if(usersService.currentlyOpenUserEmail()) {
|
||||||
<app-user-edit></app-user-edit>
|
<div cdkTrapFocus [cdkTrapFocusAutoCapture]="true">
|
||||||
|
<app-user-edit></app-user-edit>
|
||||||
|
</div>
|
||||||
|
|
||||||
} @else if(isNew) {
|
} @else if(isNew) {
|
||||||
<h1 class="mat-headline-4">New User Form</h1>
|
<div cdkTrapFocus [cdkTrapFocusAutoCapture]="true">
|
||||||
<div class="spacer"></div>
|
<h1 class="mat-headline-4">New User Form</h1>
|
||||||
<p>
|
<div class="spacer"></div>
|
||||||
<button
|
<p>
|
||||||
mat-icon-button
|
<button
|
||||||
aria-label="Back to users list"
|
mat-icon-button
|
||||||
(click)="isNew = false"
|
aria-label="Back to users list"
|
||||||
>
|
(click)="isNew = false"
|
||||||
<mat-icon>arrow_back</mat-icon>
|
>
|
||||||
</button>
|
<mat-icon>arrow_back</mat-icon>
|
||||||
</p>
|
</button>
|
||||||
<app-user-edit-form [isNew]="true" (onEditComplete)="createNewUser($event)" />
|
</p>
|
||||||
|
<app-user-edit-form
|
||||||
|
[isNew]="true"
|
||||||
|
(onEditComplete)="createNewUser($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<div class="console-app__users">
|
<div class="console-app__users">
|
||||||
<div class="console-app__users-header">
|
<div class="console-app__users-header">
|
||||||
<h1 class="mat-headline-4">Users</h1>
|
<h1 class="mat-headline-4" forceFocus>Users</h1>
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<div class="console-app__users-header-buttons">
|
<div class="console-app__users-header-buttons">
|
||||||
<button
|
<button
|
||||||
@@ -88,12 +96,7 @@
|
|||||||
<mat-icon>add</mat-icon>
|
<mat-icon>add</mat-icon>
|
||||||
Add existing user
|
Add existing user
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button mat-flat-button (click)="isNew = true" color="primary">
|
||||||
mat-flat-button
|
|
||||||
(click)="isNew = true"
|
|
||||||
aria-label="Create new user"
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
Create New User
|
Create New User
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import { UserEditFormComponent } from './userEditForm.component';
|
|||||||
selector: 'app-users',
|
selector: 'app-users',
|
||||||
templateUrl: './users.component.html',
|
templateUrl: './users.component.html',
|
||||||
styleUrls: ['./users.component.scss'],
|
styleUrls: ['./users.component.scss'],
|
||||||
standalone: true,
|
|
||||||
imports: [
|
imports: [
|
||||||
FormsModule,
|
FormsModule,
|
||||||
MaterialModule,
|
MaterialModule,
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
*matRowDef="let row; columns: displayedColumns"
|
*matRowDef="let row; columns: displayedColumns"
|
||||||
[class.rowSelected]="isRowSelected(row)"
|
[class.rowSelected]="isRowSelected(row)"
|
||||||
(click)="onClick(row)"
|
(click)="onClick(row)"
|
||||||
|
(keyup.enter)="onClick(row)"
|
||||||
|
tabindex="0"
|
||||||
></mat-row>
|
></mat-row>
|
||||||
</mat-table>
|
</mat-table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ export const columns = [
|
|||||||
selector: 'app-users-list',
|
selector: 'app-users-list',
|
||||||
templateUrl: './usersList.component.html',
|
templateUrl: './usersList.component.html',
|
||||||
styleUrls: ['./usersList.component.scss'],
|
styleUrls: ['./usersList.component.scss'],
|
||||||
standalone: true,
|
|
||||||
imports: [MaterialModule, CommonModule],
|
imports: [MaterialModule, CommonModule],
|
||||||
providers: [],
|
providers: [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,7 +6,12 @@
|
|||||||
<base href="/" />
|
<base href="/" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Google+Sans:ital,opsz,wght@0,17..18,400..700;1,17..18,400..700&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
<link
|
<link
|
||||||
href="https://fonts.googleapis.com/css2?family=Google+Sans+Text:wght@400;500;700&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Google+Sans+Text:wght@400;500;700&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
@use "@angular/material" as mat;
|
@use "@angular/material" as mat;
|
||||||
@import "app/registrar/registrarSelector.component.scss";
|
@use "app/registrar/registrarSelector.component.scss";
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
@@ -55,13 +55,13 @@ body {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
width: var(--list-item-title-width);
|
width: var(--list-item-title-width);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 14px;
|
font-size: 0.85rem;
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
color: #202124;
|
color: #202124;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.console-app__list-value {
|
.console-app__list-value {
|
||||||
font-size: 14px;
|
font-size: 0.85rem;
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
@use "sass:map";
|
@use "sass:map";
|
||||||
@use "sass:math";
|
@use "sass:math";
|
||||||
@use "@angular/material" as mat;
|
@use "@angular/material" as mat;
|
||||||
@use "@material/textfield";
|
|
||||||
|
|
||||||
$secondary-color: #80868b;
|
$secondary-color: #80868b;
|
||||||
$border-color: #dadce0;
|
$border-color: #dadce0;
|
||||||
@@ -13,7 +12,8 @@ $border-color: #dadce0;
|
|||||||
// Include the common styles for Angular Material. We include this here so that you only
|
// Include the common styles for Angular Material. We include this here so that you only
|
||||||
// have to load a single css file for Angular Material in your app.
|
// have to load a single css file for Angular Material in your app.
|
||||||
// Be sure that you only ever include this mixin once!
|
// Be sure that you only ever include this mixin once!
|
||||||
@include mat.core();
|
@include mat.elevation-classes();
|
||||||
|
@include mat.app-background();
|
||||||
|
|
||||||
$typographyConfig: mat.m2-define-typography-config(
|
$typographyConfig: mat.m2-define-typography-config(
|
||||||
$headline-1:
|
$headline-1:
|
||||||
@@ -82,16 +82,11 @@ $typographyConfig: mat.m2-define-typography-config(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Access and define a class with secondary color exposed
|
|
||||||
.secondary-text {
|
|
||||||
color: $secondary-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-xl {
|
.text-xl {
|
||||||
font-size: 18px;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
.text-l {
|
.text-l {
|
||||||
font-size: 16px;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-row:nth-child(odd) {
|
mat-row:nth-child(odd) {
|
||||||
@@ -115,6 +110,15 @@ mat-row:hover {
|
|||||||
--mat-sidenav-container-width: 280px;
|
--mat-sidenav-container-width: 280px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Access and define a class with secondary color exposed
|
||||||
|
.secondary-text {
|
||||||
|
color: #575757;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-text {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
$theme: mat.define-theme(
|
$theme: mat.define-theme(
|
||||||
(
|
(
|
||||||
color: (
|
color: (
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
runtime: nodejs20
|
runtime: nodejs20
|
||||||
service: console
|
service: console
|
||||||
|
basic_scaling:
|
||||||
|
max_instances: 10
|
||||||
build_env_variables:
|
build_env_variables:
|
||||||
GOOGLE_NODE_RUN_SCRIPTS: ''
|
GOOGLE_NODE_RUN_SCRIPTS: ''
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user