diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 46d5be10..c74652db 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,6 +8,9 @@ on: branches: - main +concurrency: + group: ${{ github.head_ref || github.ref }} + permissions: contents: read packages: write diff --git a/apps/holder/angular.json b/apps/holder/angular.json index f206fd35..e520cae8 100644 --- a/apps/holder/angular.json +++ b/apps/holder/angular.json @@ -3,9 +3,7 @@ "version": 1, "cli": { "packageManager": "pnpm", - "schematicCollections": [ - "@angular-eslint/schematics" - ], + "schematicCollections": ["@angular-eslint/schematics"], "analytics": false }, "newProjectRoot": "projects", @@ -56,7 +54,7 @@ "maximumError": "4kb" } ], - "outputHashing": "all", + "outputHashing": "none", "serviceWorker": "projects/pwa/ngsw-config.json" }, "development": { @@ -176,7 +174,7 @@ "maximumError": "4kb" } ], - "outputHashing": "all" + "outputHashing": "none" }, "development": { "optimization": false, diff --git a/apps/holder/nodemon.json b/apps/holder/nodemon.json deleted file mode 100644 index 422570fc..00000000 --- a/apps/holder/nodemon.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "watch": ["dist"], - "ext": "js,html,css", - "ignore": ["dist/assets/**/*"], - "exec": "node projects/browser-extension/update-manifest.js" -} diff --git a/apps/holder/package.json b/apps/holder/package.json index 962cf7d6..06a3d2ca 100644 --- a/apps/holder/package.json +++ b/apps/holder/package.json @@ -5,9 +5,9 @@ "init": "cp projects/pwa/src/assets/config/config.example.js projects/pwa/src/assets/config/config.js", "ng": "ng", "start:pwa": "ng serve pwa", + "start:extension": "ng build --project browser-extension --configuration development --watch", "build": "ng build --project pwa", - "start:extension": "concurrently --kill-others \"ng build --project browser-extension --configuration development --watch\" \"nodemon\"", - "start:extension-prod": "concurrently --kill-others \"ng build --project browser-extension --watch\" \"nodemon\"", + "build:extension": "ng build --project browser-extension", "api": "npx @openapitools/openapi-generator-cli generate -g typescript-angular -i http://localhost:3000/api-json -o projects/shared/api/kms --api-name-suffix=Kms --additional-properties=supportsES6=true,enumPropertyNaming=original,serviceSuffix=ApiService", "test": "ng test --no-watch --no-progress --browsers=ChromeHeadless", "lint": "ng lint", diff --git a/apps/holder/projects/browser-extension/src/app/app.config.ts b/apps/holder/projects/browser-extension/src/app/app.config.ts index 2fb93acc..93d9f7be 100644 --- a/apps/holder/projects/browser-extension/src/app/app.config.ts +++ b/apps/holder/projects/browser-extension/src/app/app.config.ts @@ -7,6 +7,8 @@ import { MAT_DIALOG_DEFAULT_OPTIONS } from '@angular/material/dialog'; import { provideOAuthClient } from 'angular-oauth2-oidc'; import { environment } from '../environments/environment'; import { ApiModule, Configuration } from '../../../shared/api/kms'; +import { AuthServiceInterface } from '../../../shared/settings/settings.component'; +import { AuthService } from './auth/auth.service'; function getConfiguration() { return new Configuration({ @@ -27,6 +29,10 @@ export const appConfig: ApplicationConfig = { provideOAuthClient(), importProvidersFrom(ApiModule, HttpClientModule), { provide: MAT_DIALOG_DEFAULT_OPTIONS, useValue: { hasBackdrop: true } }, + { + provide: AuthServiceInterface, + useClass: AuthService, + }, { provide: Configuration, useFactory: getConfiguration, diff --git a/apps/holder/projects/browser-extension/update-manifest.js b/apps/holder/projects/browser-extension/update-manifest.js deleted file mode 100644 index fcebe46f..00000000 --- a/apps/holder/projects/browser-extension/update-manifest.js +++ /dev/null @@ -1,71 +0,0 @@ -const fs = require("fs"); -const path = require("path"); - -const distPath = path.join(__dirname, "../../", "dist", "browser-extension"); // Adjust if your dist path is different -const manifestPath = path.join(distPath, "manifest.json"); - -// Function to find a specific script or style file -function findFile(pattern) { - return new Promise((resolve, reject) => { - fs.readdir(distPath, (err, files) => { - if (err) { - return reject("Failed to read dist directory: " + err); - } - const foundFile = files.find((file) => new RegExp(pattern).test(file)); - if (!foundFile) { - return reject(`${pattern} file not found.`); - } - resolve(foundFile); - }); - }); -} - -// Function to update the manifest.json -function updateManifest(backgroundScript, contentScript, styleFile) { - fs.readFile(manifestPath, "utf8", (err, data) => { - if (err) { - return console.error("Failed to read manifest.json:", err); - } - - let manifest; - try { - manifest = JSON.parse(data); - } catch (e) { - return console.error("Failed to parse manifest.json:", e); - } - - // Update the manifest with new script and style filenames - manifest.background.service_worker = backgroundScript; - manifest.content_scripts[0].js = [contentScript]; - manifest.content_scripts[0].css = [styleFile]; // Update the CSS file for content scripts - - fs.writeFile( - manifestPath, - JSON.stringify(manifest, null, 2), - "utf8", - (err) => { - if (err) { - return console.error("Failed to write updated manifest.json:", err); - } - - console.log( - "Manifest updated successfully with scripts and style:", - backgroundScript, - contentScript, - styleFile, - ); - }, - ); - }); -} - -// Execute the script and style updates -Promise.all([ - findFile("^background\\..*\\.js$"), // Regex pattern for background script - findFile("^content-script\\..*\\.js$"), // Regex pattern for content script - findFile("^styles\\..*\\.css$"), // Regex pattern for the styles -]) - .then(([backgroundScript, contentScript, styleFile]) => { - updateManifest(backgroundScript, contentScript, styleFile); - }) - .catch(console.error); diff --git a/apps/holder/projects/pwa/src/app/app.config.ts b/apps/holder/projects/pwa/src/app/app.config.ts index e028bcd0..81662b76 100644 --- a/apps/holder/projects/pwa/src/app/app.config.ts +++ b/apps/holder/projects/pwa/src/app/app.config.ts @@ -13,8 +13,9 @@ import { MAT_DIALOG_DEFAULT_OPTIONS } from '@angular/material/dialog'; import { OAuthModuleConfig, provideOAuthClient } from 'angular-oauth2-oidc'; import { environment } from '../environments/environment'; import { ApiModule, Configuration } from '../../../shared/api/kms'; -import { AuthService } from '../../../shared/auth/auth.service'; +import { AuthService } from './auth/auth.service'; import { provideServiceWorker } from '@angular/service-worker'; +import { AuthServiceInterface } from '../../../shared/settings/settings.component'; function authAppInitializerFactory( authService: AuthService @@ -47,6 +48,10 @@ export const appConfig: ApplicationConfig = { provide: Configuration, useValue: new Configuration({ basePath: environment.backendUrl }), }, + { + provide: AuthServiceInterface, + useClass: AuthService, + }, provideServiceWorker('ngsw-worker.js', { enabled: !isDevMode(), registrationStrategy: 'registerWhenStable:30000', diff --git a/apps/holder/projects/pwa/src/app/app.routes.ts b/apps/holder/projects/pwa/src/app/app.routes.ts index d78bdd18..045f1370 100644 --- a/apps/holder/projects/pwa/src/app/app.routes.ts +++ b/apps/holder/projects/pwa/src/app/app.routes.ts @@ -1,10 +1,10 @@ import { Routes } from '@angular/router'; -import { authGuard } from '../../../shared/auth/auth.guard'; +import { authGuard } from './auth/auth.guard'; import { CredentialsListComponent } from '../../../shared/credentials/credentials-list/credentials-list.component'; import { CredentialsShowComponent } from '../../../shared/credentials/credentials-show/credentials-show.component'; import { SettingsComponent } from '../../../shared/settings/settings.component'; import { ScannerComponent } from './scanner/scanner.component'; -import { LoginComponent } from '../../../shared/login/login.component'; +import { LoginComponent } from './login/login.component'; import { HistoryListComponent } from '../../../shared/history/history-list/history-list.component'; import { HistoryShowComponent } from '../../../shared/history/history-show/history-show.component'; diff --git a/apps/holder/projects/shared/auth/auth.guard.ts b/apps/holder/projects/pwa/src/app/auth/auth.guard.ts similarity index 100% rename from apps/holder/projects/shared/auth/auth.guard.ts rename to apps/holder/projects/pwa/src/app/auth/auth.guard.ts diff --git a/apps/holder/projects/shared/auth/auth.service.ts b/apps/holder/projects/pwa/src/app/auth/auth.service.ts similarity index 97% rename from apps/holder/projects/shared/auth/auth.service.ts rename to apps/holder/projects/pwa/src/app/auth/auth.service.ts index 76e7335f..acac671a 100644 --- a/apps/holder/projects/shared/auth/auth.service.ts +++ b/apps/holder/projects/pwa/src/app/auth/auth.service.ts @@ -5,10 +5,11 @@ import { Router } from '@angular/router'; import { OAuthService } from 'angular-oauth2-oidc'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { filter, map } from 'rxjs/operators'; -import { authConfig } from '../../pwa/src/app/authConfig'; +import { authConfig } from '../authConfig'; +import { AuthServiceInterface } from '../../../../shared/settings/settings.component'; @Injectable({ providedIn: 'root' }) -export class AuthService { +export class AuthService implements AuthServiceInterface { private isAuthenticatedSubject$ = new BehaviorSubject(false); public isAuthenticated$ = this.isAuthenticatedSubject$.asObservable(); diff --git a/apps/holder/projects/shared/login/login.component.html b/apps/holder/projects/pwa/src/app/login/login.component.html similarity index 100% rename from apps/holder/projects/shared/login/login.component.html rename to apps/holder/projects/pwa/src/app/login/login.component.html diff --git a/apps/holder/projects/shared/login/login.component.scss b/apps/holder/projects/pwa/src/app/login/login.component.scss similarity index 100% rename from apps/holder/projects/shared/login/login.component.scss rename to apps/holder/projects/pwa/src/app/login/login.component.scss diff --git a/apps/holder/projects/shared/login/login.component.ts b/apps/holder/projects/pwa/src/app/login/login.component.ts similarity index 100% rename from apps/holder/projects/shared/login/login.component.ts rename to apps/holder/projects/pwa/src/app/login/login.component.ts diff --git a/apps/holder/projects/shared/auth/auth.guard.spec.ts b/apps/holder/projects/shared/auth/auth.guard.spec.ts deleted file mode 100644 index f1ada9d3..00000000 --- a/apps/holder/projects/shared/auth/auth.guard.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { CanActivateFn } from '@angular/router'; - -import { authGuard } from './auth.guard'; - -describe('authGuard', () => { - const executeGuard: CanActivateFn = (...guardParameters) => - TestBed.runInInjectionContext(() => authGuard(...guardParameters)); - - beforeEach(() => { - TestBed.configureTestingModule({}); - }); - - it('should be created', () => { - expect(executeGuard).toBeTruthy(); - }); -}); diff --git a/apps/holder/projects/shared/auth/auth.service.spec.ts b/apps/holder/projects/shared/auth/auth.service.spec.ts deleted file mode 100644 index 9b4ad9b3..00000000 --- a/apps/holder/projects/shared/auth/auth.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from "@angular/core/testing"; - -import { AuthService } from "./auth.service"; - -describe("AuthService", () => { - let service: AuthService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(AuthService); - }); - - it("should be created", () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/apps/holder/projects/shared/login/login.component.spec.ts b/apps/holder/projects/shared/login/login.component.spec.ts deleted file mode 100644 index b0118559..00000000 --- a/apps/holder/projects/shared/login/login.component.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { type ComponentFixture, TestBed } from "@angular/core/testing"; - -import { LoginComponent } from "./login.component"; - -describe("LoginComponent", () => { - let component: LoginComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [LoginComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(LoginComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it("should create", () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/apps/holder/projects/shared/settings/settings.component.ts b/apps/holder/projects/shared/settings/settings.component.ts index e0535d59..d1350003 100644 --- a/apps/holder/projects/shared/settings/settings.component.ts +++ b/apps/holder/projects/shared/settings/settings.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; -import { AuthService } from '../auth/auth.service'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { SettingsService } from './settings.service'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; @@ -22,6 +21,10 @@ export declare namespace globalThis { }; } +export abstract class AuthServiceInterface { + abstract logout(): void; +} + @Component({ selector: 'app-settings', standalone: true, @@ -41,7 +44,7 @@ export class SettingsComponent implements OnInit { keycloakLink: string; constructor( - public authService: AuthService, + public authService: AuthServiceInterface, public settingsService: SettingsService, private httpClient: HttpClient, private settingsApiService: SettingsApiService diff --git a/docs/development.md b/docs/development.md index b4eb9218..7b59e40c 100644 --- a/docs/development.md +++ b/docs/development.md @@ -41,6 +41,8 @@ The command `pnpm run start:extension` in the `holder` folder will watch on the Angular is using the webpack compiler instead of the modern esbuild. This is required since we need to build multiple file like the main and background file and right now it is not possible to pass a custom esbuild config to angular. +To build the plugin for production, run `pnpm run build:extension`. The output will be in the `dist/browser-extension` folder like the start command, but the files are minified and optimized for production. + ## Backend All endpoints are available via the `http://localhost:3000` address. A swagger endpoint is available at `http://localhost:3000/api` where you can authenticate with your keycloak user credentials. Don't forget to have an `.env` file in the folder to configure the application, it will not use the `.env` file in the root folder.