@abgov/nx-adsp 12.4.1 → 12.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/generators.json +6 -0
  2. package/package.json +1 -1
  3. package/src/generators/angular-app/angular-app.js +22 -7
  4. package/src/generators/angular-app/angular-app.js.map +1 -1
  5. package/src/generators/angular-app/files/AGENTS.md__tmpl__ +96 -0
  6. package/src/generators/angular-app/files/src/app/app.component.css__tmpl__ +10 -54
  7. package/src/generators/angular-app/files/src/app/app.component.html__tmpl__ +18 -49
  8. package/src/generators/angular-app/files/src/app/app.component.spec.ts__tmpl__ +22 -27
  9. package/src/generators/angular-app/files/src/app/app.component.ts__tmpl__ +60 -20
  10. package/src/generators/angular-app/files/src/app/app.config.ts__tmpl__ +27 -0
  11. package/src/generators/angular-app/files/src/app/app.routes.ts__tmpl__ +10 -31
  12. package/src/generators/angular-app/files/src/app/home/home.component.html__tmpl__ +1 -3
  13. package/src/generators/angular-app/files/src/app/home/home.component.ts__tmpl__ +9 -23
  14. package/src/generators/angular-app/files/src/app/protected/protected.component.html__tmpl__ +2 -2
  15. package/src/generators/angular-app/files/src/app/protected/protected.component.spec.ts__tmpl__ +27 -43
  16. package/src/generators/angular-app/files/src/app/protected/protected.component.ts__tmpl__ +15 -15
  17. package/src/generators/angular-app/files/src/app/services/auth-guard.service.ts__tmpl__ +12 -21
  18. package/src/generators/angular-app/files/src/environments/environment.ts__tmpl__ +2 -11
  19. package/src/generators/angular-app/files/src/index.html__tmpl__ +3 -9
  20. package/src/generators/angular-app/files/src/main.ts__tmpl__ +8 -12
  21. package/src/generators/angular-app/files/src/silent-check-sso.html__tmpl__ +5 -0
  22. package/src/generators/angular-app/files/src/styles.css__tmpl__ +2 -2
  23. package/src/generators/express-service/files/AGENTS.md__tmpl__ +76 -0
  24. package/src/generators/mean/mean.d.ts +3 -0
  25. package/src/generators/mean/mean.js +30 -0
  26. package/src/generators/mean/mean.js.map +1 -0
  27. package/src/generators/mean/mean.spec.ts +38 -0
  28. package/src/generators/mean/schema.d.ts +11 -0
  29. package/src/generators/mean/schema.json +26 -0
  30. package/src/generators/react-app/files/AGENTS.md__tmpl__ +76 -0
  31. package/src/generators/angular-app/files/src/app/app.module.ts__tmpl__ +0 -47
  32. package/src/generators/angular-app/files/src/app/auth-callback/auth-callback.component.css__tmpl__ +0 -13
  33. package/src/generators/angular-app/files/src/app/auth-callback/auth-callback.component.html__tmpl__ +0 -12
  34. package/src/generators/angular-app/files/src/app/auth-callback/auth-callback.component.spec.ts__tmpl__ +0 -33
  35. package/src/generators/angular-app/files/src/app/auth-callback/auth-callback.component.ts__tmpl__ +0 -48
  36. package/src/generators/angular-app/files/src/app/auth.interceptor.ts__tmpl__ +0 -24
  37. package/src/generators/angular-app/files/src/app/logout/logout.component.html__tmpl__ +0 -1
  38. package/src/generators/angular-app/files/src/app/logout/logout.component.ts__tmpl__ +0 -9
  39. package/src/generators/angular-app/files/src/app/services/auth.service.ts__tmpl__ +0 -57
  40. package/src/generators/angular-app/files/src/app/tenant.service.ts__tmpl__ +0 -19
  41. package/src/generators/angular-app/files/src/environments/config.ts__tmpl__ +0 -21
  42. /package/src/generators/angular-app/files/{src → public}/assets/banner.jpg +0 -0
  43. /package/src/generators/angular-app/files/{src → public}/assets/github-1.svg +0 -0
  44. /package/src/generators/angular-app/files/{src → public}/favicon.ico +0 -0
package/generators.json CHANGED
@@ -43,6 +43,12 @@
43
43
  "factory": "./src/generators/angular-app/angular-app",
44
44
  "schema": "./src/generators/angular-app/schema.json",
45
45
  "description": "Generator that creates a Angular based frontend application."
46
+ },
47
+ "mean": {
48
+ "factory": "./src/generators/mean/mean",
49
+ "schema": "./src/generators/mean/schema.json",
50
+ "description": "Generator that creates a MEAN fullstack solution.",
51
+ "hidden": true
46
52
  }
47
53
  }
48
54
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abgov/nx-adsp",
3
- "version": "12.4.1",
3
+ "version": "12.6.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "src/index.js",
6
6
  "description": "Government of Alberta - Nx plugin for ADSP apps.",
@@ -56,6 +56,7 @@ function removeFiles(host, options) {
56
56
  }
57
57
  function default_1(host, options) {
58
58
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
59
+ var _a, _b;
59
60
  const normalizedOptions = yield normalizeOptions(host, options);
60
61
  const { applicationGenerator: initAngular } = yield Promise.resolve().then(() => require('@nx/angular/generators'));
61
62
  yield initAngular(host, {
@@ -64,17 +65,31 @@ function default_1(host, options) {
64
65
  linter: 'none',
65
66
  directory: `apps/${options.name}`
66
67
  });
67
- (0, devkit_1.addDependenciesToPackageJson)(host, {}, {
68
- '@abgov/angular-components': '^3.0.0',
69
- '@abgov/web-components': '^1.19.0',
70
- 'oidc-client': '~1.11.5',
71
- });
68
+ (0, devkit_1.addDependenciesToPackageJson)(host, {
69
+ '@abgov/angular-components': '5.2.1',
70
+ '@abgov/design-tokens': '1.8.0',
71
+ '@abgov/ui-components-common': '^2.0.0',
72
+ '@abgov/web-components': '1.39.3',
73
+ 'keycloak-angular': '^19.0.2',
74
+ 'keycloak-js': '^23.0.7',
75
+ 'zone.js': '~0.15.0',
76
+ }, {});
72
77
  const addedProxy = addFiles(host, normalizedOptions);
73
- removeFiles(host, normalizedOptions);
78
+ // @nx/angular generates app.ts/html/css/spec.ts (new naming) and nx-welcome.ts;
79
+ // our templates use app.component.* and don't use nx-welcome.
80
+ for (const file of ['app.ts', 'app.html', 'app.css', 'app.spec.ts', 'nx-welcome.ts']) {
81
+ host.delete(`${normalizedOptions.projectRoot}/src/app/${file}`);
82
+ }
74
83
  const layout = (0, devkit_1.getWorkspaceLayout)(host);
75
84
  const config = (0, devkit_1.readProjectConfiguration)(host, options.name);
76
- config.targets.build.options = Object.assign(Object.assign({}, config.targets.build.options), { assets: [
85
+ // Remove the generated fileReplacements for production — single environment.ts
86
+ // is pre-populated from tenant config at generation time.
87
+ if ((_b = (_a = config.targets.build.configurations) === null || _a === void 0 ? void 0 : _a.production) === null || _b === void 0 ? void 0 : _b.fileReplacements) {
88
+ delete config.targets.build.configurations.production.fileReplacements;
89
+ }
90
+ config.targets.build.options = Object.assign(Object.assign({}, config.targets.build.options), { polyfills: ['zone.js'], assets: [
77
91
  ...config.targets.build.options.assets,
92
+ `${normalizedOptions.projectRoot}/src/silent-check-sso.html`,
78
93
  {
79
94
  glob: 'nginx.conf',
80
95
  input: `${layout.appsDir}/${options.name}`,
@@ -1 +1 @@
1
- {"version":3,"file":"angular-app.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/angular-app/angular-app.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"angular-app.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/angular-app/angular-app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ # AGENTS.md — <%= projectName %>
2
+
3
+ Angular 19 frontend for the Alberta Digital Service Platform (ADSP).
4
+ Generated by `nx g @abgov/nx-adsp:angular-app`.
5
+
6
+ ## Stack
7
+
8
+ - **UI**: Angular 19 standalone + GoA design system (`@abgov/angular-components` — `Goab*` components)
9
+ - **Auth**: `keycloak-angular` with `keycloak-js`
10
+ - **Router**: Angular Router with `createAuthGuard` from `keycloak-angular`
11
+
12
+ ## Key files
13
+
14
+ | File | Purpose |
15
+ |------|---------|
16
+ | `src/main.ts` | Entry — bootstraps `AppComponent` with `appConfig`, imports `zone.js` |
17
+ | `src/app/app.config.ts` | `provideKeycloak` (PKCE, silent SSO), `provideRouter`, `provideHttpClient` |
18
+ | `src/app/app.component.ts` | Shell — auth state, hero banner background workaround, public API call |
19
+ | `src/app/app.routes.ts` | Routes — `/protected` with `createAuthGuard` |
20
+ | `src/app/protected/protected.component.ts` | Protected route — shows authenticated user info |
21
+ | `src/environments/environment.ts` | Access URL, realm, client ID — pre-set from ADSP tenant |
22
+
23
+ ## Auth pattern
24
+
25
+ ```typescript
26
+ import Keycloak from 'keycloak-js';
27
+ import { inject } from '@angular/core';
28
+
29
+ private keycloak = inject(Keycloak); // provided by provideKeycloak in app.config.ts
30
+
31
+ this.keycloak.authenticated // boolean
32
+ this.keycloak.tokenParsed?.['name'] // user display name
33
+ this.keycloak.login()
34
+ this.keycloak.logout({ redirectUri: window.location.origin })
35
+ ```
36
+
37
+ Route guard using `createAuthGuard` from `keycloak-angular`:
38
+
39
+ ```typescript
40
+ const authGuard = createAuthGuard(async (_route, _state, { authenticated, keycloak }) => {
41
+ if (authenticated) return true;
42
+ await keycloak.login({ redirectUri: window.location.href });
43
+ return false;
44
+ });
45
+ ```
46
+
47
+ ## GoA design system
48
+
49
+ Components are imported from `@abgov/angular-components` with the `Goab` prefix
50
+ and added to each standalone component's `imports` array:
51
+
52
+ ```typescript
53
+ import { GoabButton, GoabAppHeader } from '@abgov/angular-components';
54
+
55
+ @Component({ imports: [GoabButton, GoabAppHeader], ... })
56
+ ```
57
+
58
+ **Known limitation:** `GoabHeroBanner.backgroundUrl` does not bind reliably via
59
+ Angular's template binding due to an `@if(isReady)` timing issue inside the
60
+ component wrapper. Use `MutationObserver` to set the property directly after
61
+ the inner `<goa-hero-banner>` element appears — see `app.component.ts` for the
62
+ established pattern.
63
+
64
+ ## Backend API calls (mean / proxy setup)
65
+
66
+ If `proxy.conf.json` exists at the project root, API calls are proxied to the
67
+ backend service in development. Use relative `/api/` paths — do not hardcode
68
+ the service URL:
69
+
70
+ ```typescript
71
+ // ✓ correct — works through proxy in dev, nginx in production
72
+ this.http.get('/api/v1/my-resource')
73
+
74
+ // ✗ wrong — bypasses proxy, won't work in production
75
+ this.http.get('http://localhost:3333/my-service/v1/my-resource')
76
+ ```
77
+
78
+ `includeBearerTokenInterceptor` (configured in `app.config.ts`) automatically
79
+ attaches the Keycloak access token to all outgoing HTTP requests.
80
+ In production, `nginx.conf` contains the same proxy rule routing `/api/` to
81
+ the backend service hostname.
82
+
83
+ ## Adding a new route
84
+
85
+ 1. Create `src/app/my-feature/my-feature.component.ts` as a standalone component
86
+ 2. Add the route to `src/app/app.routes.ts`
87
+ 3. Add required `Goab*` components to the new component's `imports` array
88
+
89
+ ## What NOT to change
90
+
91
+ - `app.config.ts` — `provideKeycloak` initialises keycloak-js once; do not
92
+ create a second Keycloak instance elsewhere
93
+ - `silent-check-sso.html` — served from `public/`; required for keycloak-js
94
+ silent SSO check on page load
95
+ - `environments/environment.ts` — access URL and realm are pre-configured for
96
+ the ADSP tenant; override at runtime via environment variables
@@ -1,36 +1,16 @@
1
- .container {
2
- width: 100%;
3
- margin: 0 auto;
4
- padding: 0;
1
+ .app {
2
+ margin: 0;
5
3
  }
6
4
 
7
- @media (min-width: 640px) {
8
- .container {
9
- margin: 0 auto;
10
- padding: 3rem 1rem;
11
- width: 640px;
12
- }
5
+ main {
6
+ display: grid;
7
+ grid-template-columns: repeat(6, auto);
13
8
  }
14
- @media (min-width: 768px) {
15
- .container {
16
- margin: 0 auto;
17
- padding: 3rem 1rem;
18
- width: 768px;
19
- }
20
- }
21
- @media (min-width: 1024px) {
22
- .container {
23
- padding: 3rem 1rem;
24
- margin: 0 auto;
25
- width: 1024px;
26
- }
27
- }
28
- @media (min-width: 1280px) {
29
- .container {
30
- padding: 3rem 1rem;
31
- margin: 0 auto;
32
- width: 1280px;
33
- }
9
+
10
+ section {
11
+ grid-column: 2 / span 2;
12
+ margin-top: 40px;
13
+ margin-bottom: 40px;
34
14
  }
35
15
 
36
16
  .nextSteps {
@@ -42,27 +22,3 @@
42
22
  margin-bottom: 0;
43
23
  line-height: 50px;
44
24
  }
45
-
46
- .nextSteps li button {
47
- margin: 20px 0 0 10px;
48
- }
49
-
50
-
51
- .footer {
52
- background-color: #f1f1f1;
53
- padding-top: 56px;
54
- padding-bottom: 56px;
55
- padding-left: 150px;
56
- padding-right: 150px;
57
- display: flex;
58
- flex-direction: row;
59
- flex-wrap: wrap;
60
- border-bottom: 16px solid #0081a2;
61
- }
62
-
63
- @media (max-width: 767px) {
64
- /* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */
65
- .body-content {
66
- padding-top: 50px;
67
- }
68
- }
@@ -1,58 +1,27 @@
1
1
  <div class="app">
2
- <goa-microsite-header type="alpha"></goa-microsite-header>
3
- <goa-app-header heading="Digital Service Example" url="/"></goa-app-header>
4
- <goa-hero-banner
5
- backgroundUrl="../assets/banner.jpg"
6
- heading="Quick start of a digital service"
7
- >
8
- </goa-hero-banner>
9
- <main class="container">
2
+ <goab-microsite-header type="alpha"></goab-microsite-header>
3
+ <goab-app-header url="/" heading="<%= projectName %>">
4
+ <goab-button-group alignment="end">
5
+ @if (userName) { <span>{{ userName }}</span> }
6
+ @if (!isLoggedIn) {
7
+ <goab-button type="tertiary" (onClick)="login()">Sign in</goab-button>
8
+ } @else {
9
+ <goab-button type="tertiary" (onClick)="logout()">Sign out</goab-button>
10
+ }
11
+ </goab-button-group>
12
+ </goab-app-header>
13
+ <goab-hero-banner #heroBanner heading="<%= projectName %>"></goab-hero-banner>
14
+ <main>
10
15
  <section>
11
16
  <h2>Welcome to {{ title }}!</h2>
12
- <p>
13
- Don't panic. Start editing the project to build your digital service.
14
- </p>
17
+ <p>Don't panic. Start editing the project to build your digital service.</p>
15
18
  <h3>A few things you might want to do next:</h3>
16
19
  <ul class="nextSteps">
17
- <li>Create the 'my-app' client in your realm to let users sign in</li>
18
- <li>
19
- Make requests to the backend API by either updating nginx.conf or
20
- enabling CORS on the API.
21
- </li>
22
- <li>Add requests to public API resources:</li>
23
- <li>Add requests to private API resources:</li>
20
+ <li>Register the '<%= projectName %>' client in your realm and set CLIENT_SECRET.</li>
21
+ <li>Add requests to public API resources: {{ publicResource }}</li>
22
+ <li>Add requests to private API resources: {{ privateResource }}</li>
24
23
  </ul>
25
24
  </section>
26
- <div class="container-fluid">
27
- <div class="row">
28
- <h3><a routerLink="/">Home</a> | <a routerLink="/protected">Admin Area</a></h3>
29
- <div *ngIf="isLoggedIn(); else elseBlock">
30
- <goa-button (click)="logout()">
31
- Sign Out
32
- </goa-button>
33
- </div>
34
- <ng-template #elseBlock>
35
- <goa-button (click)="login()">
36
- Sign In
37
- </goa-button>
38
- </ng-template>
39
- <div class="col-sm-12 body-content">
40
- <router-outlet></router-outlet>
41
- </div>
42
- </div>
43
- </div>
25
+ <router-outlet></router-outlet>
44
26
  </main>
45
- <footer class="footer">
46
- <div class="goa-socialconnect">
47
- <div class="goa-title">Connect with us on</div>
48
- <ul>
49
- <div>
50
- <img src="./assets/github-1.svg" height="15px" />
51
- <a href="https://github.com/abgov" rel="noreferrer" target="_blank">
52
- GitHub
53
- </a>
54
- </div>
55
- </ul>
56
- </div>
57
- </footer>
58
27
  </div>
@@ -1,44 +1,39 @@
1
- import { TestBed, getTestBed } from '@angular/core/testing';
1
+ import { ElementRef } from '@angular/core';
2
+ import { TestBed } from '@angular/core/testing';
3
+ import { provideRouter } from '@angular/router';
4
+ import { GoabAppHeader, GoabButton, GoabButtonGroup, GoabHeroBanner, GoabMicrositeHeader } from '@abgov/angular-components';
5
+ import Keycloak from 'keycloak-js';
2
6
  import { AppComponent } from './app.component';
3
- import { AngularComponentsModule } from '@abgov/angular-components';
4
- import { RouterTestingModule } from '@angular/router/testing';
5
- import {
6
- BrowserDynamicTestingModule,
7
- platformBrowserDynamicTesting
8
- } from "@angular/platform-browser-dynamic/testing";
9
-
10
7
 
11
8
  describe('AppComponent', () => {
12
- beforeAll( ()=> {
13
- TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
14
- });
15
- beforeEach(async () => {
16
- let envData = {"production":false,"access":{"url":"https://testurl.com","realm":"123","client_id":"urn:ads:platform:tenant-admin-app"},"tenantApi":{"host":"http://localhost:3333","endpoints":{"tenantNameByRealm":"/api/tenant/v1/realm"}}}
9
+ const keycloakMock = {
10
+ authenticated: false,
11
+ tokenParsed: null,
12
+ login: jasmine.createSpy('login'),
13
+ logout: jasmine.createSpy('logout'),
14
+ };
17
15
 
18
- localStorage.setItem('envData', JSON.stringify(envData));
16
+ beforeEach(async () => {
19
17
  await TestBed.configureTestingModule({
20
- declarations: [AppComponent],
21
- imports: [AngularComponentsModule, RouterTestingModule],
18
+ imports: [AppComponent, GoabAppHeader, GoabButton, GoabButtonGroup, GoabHeroBanner, GoabMicrositeHeader],
19
+ providers: [
20
+ provideRouter([]),
21
+ { provide: Keycloak, useValue: keycloakMock },
22
+ { provide: ElementRef, useValue: { nativeElement: { querySelector: () => null } } },
23
+ ],
22
24
  }).compileComponents();
23
25
  });
24
26
 
25
27
  it('should create the app', () => {
26
28
  const fixture = TestBed.createComponent(AppComponent);
27
- const app = fixture.componentInstance;
28
- expect(app).toBeTruthy();
29
- });
30
-
31
- it(`should have as title '<%= projectName %>'`, () => {
32
- const fixture = TestBed.createComponent(AppComponent);
33
- const app = fixture.componentInstance;
34
- expect(app.title).toEqual('<%= projectName %>');
29
+ expect(fixture.componentInstance).toBeTruthy();
35
30
  });
36
31
 
37
- it('should render title', () => {
32
+ it('should have welcome heading', () => {
38
33
  const fixture = TestBed.createComponent(AppComponent);
39
34
  fixture.detectChanges();
40
- const compiled = fixture.nativeElement;
41
- expect(compiled.querySelector('h2').textContent).toContain(
35
+ const compiled = fixture.nativeElement as HTMLElement;
36
+ expect(compiled.querySelector('h2')?.textContent).toContain(
42
37
  'Welcome to <%= projectName %>!'
43
38
  );
44
39
  });
@@ -1,36 +1,76 @@
1
- import { Component, OnDestroy, OnInit } from '@angular/core';
2
- import { AuthService } from './services/auth.service';
3
- import { environment } from '../environments/environment'
4
-
1
+ import { AfterViewInit, Component, ElementRef, inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
2
+ import { HttpClient } from '@angular/common/http';
3
+ import { RouterOutlet } from '@angular/router';
4
+ import {
5
+ GoabAppHeader,
6
+ GoabButton,
7
+ GoabButtonGroup,
8
+ GoabHeroBanner,
9
+ GoabMicrositeHeader,
10
+ } from '@abgov/angular-components';
11
+ import Keycloak from 'keycloak-js';
12
+ import { getAccessToken } from './user.slice';
5
13
 
6
14
  @Component({
7
- selector: '<%= projectName %>-app-root',
15
+ selector: '<%= projectName %>-root',
16
+ standalone: true,
17
+ imports: [RouterOutlet, GoabAppHeader, GoabButton, GoabButtonGroup, GoabHeroBanner, GoabMicrositeHeader],
8
18
  templateUrl: './app.component.html',
9
- styleUrls: ['./app.component.css'],
19
+ styleUrl: './app.component.css',
10
20
  })
21
+ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
22
+ title = '<%= projectName %>';
11
23
 
12
- export class AppComponent implements OnInit, OnDestroy {
13
- title = "<%= projectName %>";
24
+ @ViewChild('heroBanner', { read: ElementRef, static: false }) heroBannerRef!: ElementRef;
14
25
 
15
- constructor(private authService: AuthService) {}
26
+ private http = inject(HttpClient);
27
+ private keycloak = inject(Keycloak);
28
+ private heroObserver?: MutationObserver;
16
29
 
17
- isLoggedIn(): boolean {
18
- return this.authService.isLoggedIn()
19
- }
30
+ publicResource = 'Not retrieved';
31
+ privateResource = 'Not retrieved';
32
+
33
+ get isLoggedIn(): boolean { return this.keycloak.authenticated ?? false; }
34
+ get userName(): string { return (this.keycloak.tokenParsed?.['name'] as string) ?? ''; }
20
35
 
21
- logout() {
22
- if (this.authService.isLoggedIn()) {
23
- this.authService.logout();
36
+ ngOnInit() {
37
+ this.http.get<{ message: string }>('/api/v1/public').subscribe({
38
+ next: (data) => (this.publicResource = data.message),
39
+ error: () => (this.publicResource = 'Error loading data'),
40
+ });
41
+ if (this.isLoggedIn) {
42
+ getAccessToken().then((token) => {
43
+ this.http.get<{ message: string }>('/api/v1/private', {
44
+ headers: { Authorization: `Bearer ${token}` },
45
+ }).subscribe({
46
+ next: (data) => (this.privateResource = data.message),
47
+ error: () => (this.privateResource = 'Error loading data'),
48
+ });
49
+ });
24
50
  }
25
51
  }
26
52
 
27
- login() {
28
- if (!this.authService.isLoggedIn()) {
29
- this.authService.startAuthentication();
53
+ ngAfterViewInit() {
54
+ const host = this.heroBannerRef?.nativeElement;
55
+ if (!host) return;
56
+ const setBackground = (inner: Element) => {
57
+ (inner as any).backgroundurl = '/assets/banner.jpg';
58
+ this.heroObserver?.disconnect();
59
+ };
60
+ const existing = host.querySelector('goa-hero-banner');
61
+ if (existing) {
62
+ setBackground(existing);
63
+ } else {
64
+ this.heroObserver = new MutationObserver(() => {
65
+ const inner = host.querySelector('goa-hero-banner');
66
+ if (inner) setBackground(inner);
67
+ });
68
+ this.heroObserver.observe(host, { childList: true, subtree: true });
30
69
  }
31
70
  }
32
71
 
33
- ngOnInit() {}
72
+ ngOnDestroy() { this.heroObserver?.disconnect(); }
34
73
 
35
- ngOnDestroy(): void {}
74
+ login() { this.keycloak.login(); }
75
+ logout() { this.keycloak.logout({ redirectUri: window.location.origin }); }
36
76
  }
@@ -0,0 +1,27 @@
1
+ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
2
+ import { provideRouter } from '@angular/router';
3
+ import { provideHttpClient, withInterceptors } from '@angular/common/http';
4
+ import { includeBearerTokenInterceptor, provideKeycloak } from 'keycloak-angular';
5
+
6
+ import { routes } from './app.routes';
7
+ import { environment } from '../environments/environment';
8
+
9
+ export const appConfig: ApplicationConfig = {
10
+ providers: [
11
+ provideZoneChangeDetection({ eventCoalescing: true }),
12
+ provideRouter(routes),
13
+ provideHttpClient(withInterceptors([includeBearerTokenInterceptor])),
14
+ provideKeycloak({
15
+ config: {
16
+ url: environment.access.url,
17
+ realm: environment.access.realm,
18
+ clientId: environment.access.client_id,
19
+ },
20
+ initOptions: {
21
+ onLoad: 'check-sso',
22
+ pkceMethod: 'S256',
23
+ silentCheckSsoRedirectUri: `${window.location.origin}/silent-check-sso.html`,
24
+ },
25
+ }),
26
+ ],
27
+ };
@@ -1,34 +1,13 @@
1
- import { LogoutComponent } from './logout/logout.component';
2
- import { NgModule } from '@angular/core';
3
- import { Routes, RouterModule } from '@angular/router';
4
- import { HomeComponent } from './home/home.component';
1
+ import { Routes } from '@angular/router';
2
+ import { createAuthGuard } from 'keycloak-angular';
5
3
  import { ProtectedComponent } from './protected/protected.component';
6
- import { AuthGuardService } from './services/auth-guard.service';
7
- import { AuthCallbackComponent } from './auth-callback/auth-callback.component';
8
4
 
9
- const routes: Routes = [
10
- {
11
- path: '',
12
- component: HomeComponent,
13
- children: [],
14
- },
15
- {
16
- path: 'protected',
17
- component: ProtectedComponent,
18
- canActivate: [AuthGuardService],
19
- },
20
- {
21
- path: 'auth-callback',
22
- component: AuthCallbackComponent,
23
- },
24
- {
25
- path: 'signout/callback',
26
- component: LogoutComponent,
27
- },
28
- ];
5
+ const authGuard = createAuthGuard(async (_route, _state, { authenticated, keycloak }) => {
6
+ if (authenticated) return true;
7
+ await keycloak.login({ redirectUri: window.location.href });
8
+ return false;
9
+ });
29
10
 
30
- @NgModule({
31
- imports: [RouterModule.forRoot(routes)],
32
- exports: [RouterModule],
33
- })
34
- export class AppRoutingModule {}
11
+ export const routes: Routes = [
12
+ { path: 'protected', component: ProtectedComponent, canActivate: [authGuard] },
13
+ ];
@@ -1,5 +1,3 @@
1
1
  <hr />
2
2
  <h1>Public Area</h1>
3
- <div>Making a Public API call to uptime endpoint</div>
4
- <div>Uptime of API in seconds: {{uptime}}</div>
5
-
3
+ <p>Public API response: {{ publicMessage() }}</p>
@@ -1,33 +1,19 @@
1
- import { Component, OnInit } from '@angular/core';
1
+ import { Component, inject, OnInit, signal } from '@angular/core';
2
2
  import { HttpClient } from '@angular/common/http';
3
3
 
4
4
  @Component({
5
- selector: '<%= projectName %>-app-logout',
5
+ selector: '<%= projectName %>-home',
6
+ standalone: true,
6
7
  templateUrl: 'home.component.html',
7
8
  })
8
9
  export class HomeComponent implements OnInit {
9
- constructor(private http: HttpClient) {}
10
- public uptime = 0;
11
-
12
- private getHealthUrl = `${this.configData().tenantApi.host}/health`;
13
-
14
- getHealth() {
15
- const uptimePromise = this.http.get(this.getHealthUrl);
16
-
17
- uptimePromise.subscribe(
18
- (data: any) => {
19
- this.uptime = data.uptime;
20
- },
21
- (err) => console.error(err),
22
- () => console.log('done loading uptime')
23
- );
24
- }
25
-
26
- configData () {
27
- return JSON.parse(localStorage.getItem('envData') || "\"\"");
28
- }
10
+ private http = inject(HttpClient);
11
+ publicMessage = signal('Not retrieved');
29
12
 
30
13
  ngOnInit() {
31
- this.getHealth();
14
+ this.http.get<{ message: string }>('/api/v1/public').subscribe({
15
+ next: (data) => this.publicMessage.set(data.message),
16
+ error: () => this.publicMessage.set('Error loading data'),
17
+ });
32
18
  }
33
19
  }
@@ -1,4 +1,4 @@
1
1
  <hr />
2
2
  <h1>Admin Area</h1>
3
- <div>Making a Protected API call to tenant endpoint using access token</div>
4
- <div>Tenant Name: {{tenant.name}}</div>
3
+ <p>Welcome, {{ userName }}.</p>
4
+ <p>Private API response: {{ privateMessage() }}</p>