# Portal Portal manages scripts, repositories, credentials, files, and scheduled jobs. Its Angular client is bundled into an executable Spring Boot JAR with embedded Tomcat. ## Requirements | Component | Configuration | | --- | --- | | Java | JDK 21; check the active runtime with `mvn -v` | | Maven | Maven 3.9.x; Jenkins uses `maven-3.9.16` | | Backend | Spring Boot 4.1.1, embedded Tomcat 11.0.25 | | Frontend | Angular 22; Node.js 24.20.0 and npm 12.0.2 provisioned by Maven | | Default database | Persistent H2 in MySQL compatibility mode | | External database | MySQL Server 8.4+ with Connector/J 26.7.0 | Versions are defined in [pom.xml](pom.xml) and [portal_client/package.json](portal_client/package.json). The frontend lockfile pins the installed npm dependency tree. Building requires access to the Locusworks Nexus repository, npm registry, and dependency-check data services. A global Node.js or npm installation is not required for Maven builds. ## Build and run Add your SSH public key to your Gitea account, then clone and build: ```sh git clone ssh://gitea@gitea.locusworks.net:7999/locusworks/portal-webapp.git cd portal-webapp mvn clean install java -jar portal_webapp/target/portal_webapp-1.0.0-RELEASE.jar ``` Open **http://localhost:8080/portal/**. The entry page redirects to `/portal/client/`, which uses hash-based routing. Use the corresponding JAR filename if the project version has changed. The build installs frontend dependencies with `npm ci`, builds and tests the client, compiles and tests the Java modules, runs dependency checks, packages the executable JAR, and installs Maven artifacts into the local repository. It does not publish to Nexus or start the application. ### Maven settings The POM defines the `locusworks-public` dependency/plugin repository and the `nexus-release` and `nexus-snapshot` deployment repositories. If authentication is required, configure matching server IDs in your user Maven settings (`~/.m2/settings.xml`, or `%USERPROFILE%\.m2\settings.xml` on Windows): ```xml locusworks-public ${env.NEXUS_USERNAME} ${env.NEXUS_PASSWORD} ``` Supply those environment variables before building. Add `nexus-release` and `nexus-snapshot` server entries when publishing. The Nexus base URL defaults to `https://nexus.locusworks.net` and can be overridden with `-Dnexus.repo=https://your-nexus-host`. ## Application configuration Portal resolves its writable home directory in this order: 1. JVM property `-Dportal.home=/path/to/portal`. 2. Environment variable `PORTAL_HOME`. 3. `${user.home}/.portal`. ```sh java -Dportal.home=/var/lib/portal -jar portal_webapp/target/portal_webapp-1.0.0-RELEASE.jar ``` On Windows, use an absolute path such as `-Dportal.home=D:/Portal/data`. Put JVM properties before `-jar`. The home directory holds `portal.properties`, the AES seed, `portal-loggers.properties`, logs, temporary key files, and the default H2 database under `data/`. Keep it separate from build output and preserve it when replacing the JAR. Encrypted settings depend on the AES seed, so back them up together. | File | Purpose | | --- | --- | | [application.properties](portal_webapp/src/main/resources/application.properties) | Spring Boot context path, session, multipart, and JPA settings | | [portal.properties](portal_webapp/src/main/resources/portal.properties) | Defaults used to initialize and reconcile the persistent Portal configuration | For another HTTP port, append `--server.port=8081` to the Java command. The frontend assumes the `/portal` context path; changing it also requires updating client paths and rebuilding. ### Databases New installations default to `dbType=h2`. H2 uses a persistent file database and needs no separate database server. Existing installations retain their configured `dbType`; there is no automatic production switch to MySQL. For external MySQL, set `dbType=mysql` and configure `dbHost`, `dbPort`, `dbUsername`, `dbPassword`, `dbRootUser`, and `dbRootPassword` in the persistent Portal settings. Runtime migrations use the root connection; normal application access uses the application connection. H2 has separate `h2Url`, `h2Username`, and `h2Password` settings. Nonblank database passwords in `portal.properties` are read as encrypted values. Use Portal's configuration facilities to save encrypted credentials instead of putting plaintext in those fields. Connector/J 26.7.0 requires MySQL Server 8.4 or newer; see the [MySQL release notes](https://dev.mysql.com/doc/relnotes/connector-j/en/news-26-7-0.html). ## Frontend development Build the complete project once to provision the pinned tools. From `portal_client`, use the local executables to avoid picking up a different global npm version. PowerShell: ```powershell .\node\node.exe .\node\node_modules\npm\bin\npm-cli.js ci .\node\node.exe .\node\node_modules\npm\bin\npm-cli.js run build .\node\node.exe .\node\node_modules\npm\bin\npm-cli.js test ``` Linux/macOS: ```sh ./node/node ./node/node_modules/npm/bin/npm-cli.js ci ./node/node ./node/node_modules/npm/bin/npm-cli.js run build ./node/node ./node/node_modules/npm/bin/npm-cli.js test ``` With matching Node.js and npm versions on `PATH`, the equivalent commands are `npm ci`, `npm run build`, and `npm test`. The `start` script runs the Angular development server, but this project has no backend proxy configuration; API calls and realtime connections expect the `/portal` backend on the same origin. Use the packaged application for an integrated local run. Client output goes to `portal_client/dist/` and is copied into the backend JAR at build time. Rebuild the backend to include changed client assets. Login, dashboard, and feature pages load on demand; the initial-bundle warning budget remains 500 kB. The `allowScripts` entries in `package.json` approve specific versions of native build helpers. Review and renew them when upgrading those dependencies. SockJS is explicitly allowed as a CommonJS dependency for the existing transport. STOMP uses its shipped ESM entry through the TypeScript path mapping because its 7.3.0 browser export selects UMD. npm warnings and failures remain visible; routine npm notices are omitted. ## Tests and dependency checks Run the complete verification with `mvn clean install`. To run the datasource test and its prerequisite Java modules: ```sh mvn -pl portal_common -am test ``` The datasource test uses isolated in-memory H2 through `portal.database.*` JVM overrides and clears them afterward. It does not test an external MySQL instance. Frontend tests use Vitest through Angular CLI, including lazy login rendering and authentication redirects. Java test results are written to each module's `target/surefire-reports/` directory. OWASP Dependency-Check runs during `verify`, which is included in `install`. HTML reports are written to `target/dependency-check-report.html` in the root and module directories. NVD, RetireJS, and npm lockfile auditing remain enabled. The npm audit includes development dependencies; the Node Package Analyzer skips them to avoid warnings about uninstalled native binaries for other platforms. The POM sets `failOnError=false` for Dependency-Check, so review its output and reports even when Maven succeeds. Sonatype OSS Index requires credentials and is opt-in. Add a server named `oss-index` to Maven settings with your account username and API token as the password, then run: ```sh mvn clean install -Dportal.ossIndexEnabled=true ``` ## Deployment Deploy `portal_webapp/target/portal_webapp-.jar` and run it with Java 21 under your service manager. A separately installed Tomcat is not required. Configure a persistent Portal home writable by the service account. [scripts/deploy-server.sh](scripts/deploy-server.sh) copies the built JAR to `deploy/portal.jar` by default. `PORTAL_DEPLOY_DIR` changes the destination; `PORTAL_SERVICE` requests a systemd service restart after copying. In this helper, `PORTAL_HOME` refers to the **source checkout**, unlike the application's runtime data directory. Use `-Dportal.home` in the Java service command to keep those locations distinct. ### Reverse proxy Terminate HTTPS at NGINX or Apache and preserve `/portal/` when forwarding to the backend on port 8080. Realtime endpoints need WebSocket upgrades as well as ordinary HTTP forwarding. For NGINX, place the `map` in the `http` context and the `location` inside your site's HTTPS `server` block: ```nginx map $http_upgrade $connection_upgrade { default upgrade; '' close; } # Inside the HTTPS server block: location /portal/ { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 600s; } ``` Supply your hostname, TLS certificates, and HTTP-to-HTTPS redirect in the surrounding configuration. Match proxy upload limits to the application's limits (10 MB by default). See [NGINX WebSocket proxying](https://nginx.org/en/docs/http/websocket.html). For Apache, enable `mod_proxy` and `mod_proxy_http` and configure WebSocket forwarding for your installed version; see the [Apache proxy documentation](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html). ## Jenkins [Jenkinsfile](Jenkinsfile) defines a multibranch pipeline that checks out source, assigns a build version, updates a shared OWASP cache, runs `mvn clean verify`, publishes Java test results, archives JARs, and conditionally publishes Maven artifacts to Nexus. The `Build LaTeX` stage runs only on branches matching `release/**` and compiles `docs/src/portal.tex` with three `pdflatex` passes to resolve references and the table of contents. The agent needs `pdflatex` on `PATH` and the document's LaTeX packages installed; neither `latexmk` nor Perl is required. Compilation errors fail the stage. The same stage saves `target/latex/portal.pdf` with fingerprinting before workspace cleanup. Every successful `release/**` build records a release with an immutable Git tag named `portal-release-${BUILD_VERSION}`. The `Build LaTeX` stage also generates and archives `target/release-notes.md`, listing commit subjects and hashes since the nearest earlier release tag reachable from the current commit. The first recorded release includes the full history; a rebuild without new commits reports no source changes. Before compiling, the same stage uses `awk` to generate `docs/src/appendix/ReleaseNotes.tex`, which `portal.tex` includes immediately after the Revisions appendix. The generated TeX is archived alongside the PDF and Markdown. Commit text is escaped for LaTeX. Local builds use a placeholder until release notes are generated. The agent needs `awk` on its path. Notes are generated from Git history, so their detail depends on the commit messages. The final `Tag release` stage pushes the tag only after the preceding build, PDF, and applicable Maven deployment stages succeed. Notes archived before a later failure are build artifacts, not a completed release marker. Failed builds do not publish a new release tag. The helper fetches release tags and expands shallow history before comparison. Jenkins uses HTTPS checkout and the Git plugin's `gitUsernamePassword` binding to reuse the checkout SCM username/password credential for fetching and pushing release tags. The credential must have permission to push tags (a Gitea access token may be stored as its password). Git uses the SCM tool configuration, falling back to `Default`. No SSH Agent plugin is required. Tag conflicts fail the build; existing remote tags are never overwritten. Existing releases without this tag prefix are not comparison baselines. | Branch | Version | Publish to Nexus | | --- | --- | --- | | `release/` | `.-RELEASE` | Yes | | `develop` | `develop.-SNAPSHOT` | Yes | | Other branches | `0.0.--SNAPSHOT` | No | The Jenkins agent needs a shell environment, JDK 21, Maven installation `maven-3.9.16`, and managed settings configuration `locusworks-settings`. Dependency-Check uses the existing `nvdApiKey` configuration in `pom.xml`; no separate Jenkins NVD credential is required. The pipeline uses the `owasp-nvd-cache` lock. Its deploy stage publishes artifacts; it does not restart an application server.