diff --git a/.gitignore b/.gitignore index f872911..bf555f7 100644 --- a/.gitignore +++ b/.gitignore @@ -264,4 +264,5 @@ __pycache__/ # SonarQube .scannerwork/ -.sonarlint/ \ No newline at end of file +.sonarlint/ +src/MaksIT.WebUI/public/pdf.worker.min.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4ca0d54 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,74 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [3.3.4] - 2026-04-01 + +### Added + +- `MaksIT.Webapi.Tests`: service-level unit tests (settings, cache, identity, agent, account, certs flow) and domain tests for `Settings`. +- Postman collections under `src/Postman` updated to match current `MaksIT.Webapi` routes, JWT flow, and cache endpoints. + +### Fixed + +- WebUI Terms of Service (Let's Encrypt): PDF viewer loads `pdfjs-dist` worker from a Vite-bundled asset (`pdf.worker.min.mjs?url`) so rendering works in dev and production instead of failing on missing or wrong worker URLs. +- `AccountService.PatchAccountAsync` returns the account built from the cache after reload, not a stale in-memory instance. + +## [3.3.3] - 2025-12-20 + +### Changed + +- Relicensed project from GPL-3.0 to Apache-2.0. + +## [3.3.2] - 2025-12-20 + +### Changed + +- Minimal Helm chart and documentation improvements. + +## [3.3.1] - 2025-11-22 + +### Changed + +- Public release following the v3.3.0 pre-release. + +## [3.3.0] - 2025-11-15 + +### Changed + +- Pre-release of the v3.3.x line. + +## [3.2.0] - 2025-09-11 + +### Added + +- New WebUI with authentication. + +## [3.1.0] - 2024-08-11 + +### Changed + +- Stabilized release following v3.0.0. + +## [3.0.0] - 2024-05-31 + +### Added + +- WebAPI and containerization. + +## [2.0.0] - 2019-11-01 + +### Changed + +- Dependency injection pattern implementation. + +## [1.0.0] - 2019-06-29 + +### Added + +- Initial release. + + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..40c1bc5 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,53 @@ +# Contributing to MaksIT.CertsUI + +Thank you for your interest in improving this project. This document describes how to set up a development environment, what we expect from contributions, and where to get help. + +## License + +By contributing, you agree that your contributions will be licensed under the same terms as the project. See [LICENSE.md](LICENSE.md) (Apache License 2.0). + +## What to contribute + +Useful contributions include bug fixes, documentation improvements, Helm chart updates, and small, focused feature changes that fit the architecture described in [README.md](README.md) (single agent, HTTP-01, and related limitations). + +Large or architectural changes are best discussed first (see [Contact](#contact)) so effort aligns with project goals. + +## Development setup + +### Prerequisites + +- [.NET SDK](https://dotnet.microsoft.com/download) compatible with the `TargetFramework` values in the `.csproj` files under `src/` (the main solution currently targets **.NET 10**). +- Optional but recommended for end-to-end checks: **Docker** or **Podman**, as in the README installation sections. +- **Visual Studio 2022** or another editor with C# support works well; the solution file is `src/MaksIT.CertsUI.sln`. + +### Build + +From the repository root: + +```bash +dotnet build src/MaksIT.CertsUI.sln -c Release +``` + +Use `Debug` while iterating locally if you prefer. + +### Run the stack locally + +Follow [README.md](README.md) for Podman Compose, Docker Compose, or Kubernetes (Helm). That is the supported way to exercise the WebAPI, WebUI, and reverse proxy together. + +There is no separate automated test project in this repository today; manual verification through the WebUI and your compose or cluster setup is the practical check for most changes. + +## Pull requests + +1. **Branch from the branch the maintainers use for integration** (often `dev` or `main`—check the default on the host repository). +2. **Keep changes scoped**—one logical fix or feature per PR makes review and history easier. +3. **Describe the change** in the PR: what problem it solves, how you tested it, and any operational impact (config, Helm values, images). +4. **Update [CHANGELOG.md](CHANGELOG.md)** when the change is user-visible (behavior, security, deployment, or notable docs). Add entries under a new version heading or an `[Unreleased]` section at the top, following the existing [Keep a Changelog](https://keepachangelog.com/) style. +5. **Avoid unrelated formatting or drive-by refactors** in the same PR as functional changes. + +## Security issues + +Please do not open a public issue for undisclosed security vulnerabilities. Report them privately using the contact in [README.md](README.md) (Contact section) so they can be handled responsibly. + +## Contact + +Questions and coordination: see **Contact** in [README.md](README.md). diff --git a/README.md b/README.md index c56bfd5..21e9d2c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # MaksIT.CertsUI – Modern container-native ACME client with a full WebUI experience +![Line Coverage](assets/badges/coverage-lines.svg) ![Branch Coverage](assets/badges/coverage-branches.svg) ![Method Coverage](assets/badges/coverage-methods.svg) + MaksIT.CertsUI is a powerful, container-native ACMEv2 client built to simplify and automate the entire lifecycle of HTTPS certificates issued by Let’s Encrypt. It is an independent, unofficial project and is not affiliated with or endorsed by Let’s Encrypt or ISRG. Designed for modern infrastructure, it combines a robust WebAPI, intuitive WebUI, and lightweight edge Agent to deliver fully automated certificate issuance, renewal, and deployment across Docker, Podman, and Kubernetes environments. MaksIT.CertsUI supports the HTTP-01 challenge and follows the official [Let’s Encrypt guidelines](https://letsencrypt.org/docs/) while implementing recommended security and operational best practices. @@ -18,7 +20,8 @@ If you find this project useful, please consider supporting its development: - [MaksIT.CertsUI – Modern container-native ACME client with a full WebUI experience](#maksitcertsui--modern-container-native-acme-client-with-a-full-webui-experience) - [Table of Contents](#table-of-contents) - - [Versions History](#versions-history) + - [Changelog](#changelog) + - [Contributing](#contributing) - [Architecture](#architecture) - [Current Limitations](#current-limitations) - [Architecture Scheme](#architecture-scheme) @@ -43,18 +46,13 @@ If you find this project useful, please consider supporting its development: - [Contact](#contact) -## Versions History +## Changelog -* 29 Jun, 2019 - V1.0.0 -* 01 Nov, 2019 - V2.0.0 (Dependency Injection pattern implementation) -* 31 May, 2024 - V3.0.0 (Webapi and containerization) -* 11 Aug, 2024 - V3.1.0 (Release) -* 11 Sep, 2025 - V3.2.0 New WebUI with authentication -* 15 Nov, 2025 - V3.3.0 Pre release -* 22 Nov, 2025 - V3.3.1 Public release -* 20 Dec, 2025 - V3.3.2 Minimal helm chart and documentation improvements -* 20 Dec, 2025 - V3.3.3 Relicense project from GPL-3.0 to Apache-2.0 +Version history and release notes live in [CHANGELOG.md](CHANGELOG.md). +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, pull request expectations, and security reporting. --- @@ -619,7 +617,9 @@ Helm OCI support enables you to pull and install Helm charts directly from conta ### 2. Prepare Namespace, Secrets, and ConfigMap -Before installing the Helm chart, create a dedicated namespace and provide the required secrets and configuration for the MaksIT.CertsUI Webapi. +By default, the chart creates the server Secret, server ConfigMap, and client ConfigMap from Helm values (`certsServerSecrets`, `certsServerConfig`, `certsClientRuntime`) as defined in [`src/helm/values.yaml`](src/helm/values.yaml). Set those keys in your `custom-values.yaml` (see the next section) and you can skip the manual `kubectl` resources below. + +If you prefer to manage Secrets and ConfigMaps yourself, create them in the namespace and point the chart at them with `components.server.secretsFile.existingSecret`, `components.server.configMapFile.existingConfigMap`, and `components.client.configMapFile.existingConfigMap` (leave the templated `content` unused for that component). **Step 1: Create Namespace** @@ -629,16 +629,18 @@ kubectl create namespace certs-ui **Step 2: Create the Secret (`appsecrets.json`)** -Replace the placeholder values with your actual secrets. This secret contains authentication and agent keys required by the Webapi. +Replace the placeholder values with your actual secrets. This secret contains authentication and agent keys required by the Webapi (same shape as the chart’s templated `appsecrets.json`). ```json { - "Auth": { - "Secret": "", - "Pepper": "" -}, - "Agent": { - "AgentKey": "" + "Configuration": { + "Auth": { + "Secret": "", + "Pepper": "" + }, + "Agent": { + "AgentKey": "" + } } } ``` @@ -646,12 +648,14 @@ Replace the placeholder values with your actual secrets. This secret contains au ```bash kubectl create secret generic certs-ui-server-secrets \ --from-literal=appsecrets.json='{ - "Auth": { - "Secret": "", - "Pepper": "" - }, - "Agent": { - "AgentKey": "" + "Configuration": { + "Auth": { + "Secret": "", + "Pepper": "" + }, + "Agent": { + "AgentKey": "" + } } }' \ -n certs-ui @@ -669,10 +673,11 @@ Edit the values as needed for your environment. This configmap contains applicat { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } -}, + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", "Configuration": { "Auth": { "Issuer": "", @@ -704,6 +709,7 @@ kubectl create configmap certs-ui-server-configmap \ "Microsoft.AspNetCore": "Warning" } }, + "AllowedHosts": "*", "Configuration": { "Auth": { "Issuer": "", @@ -730,91 +736,92 @@ kubectl create configmap certs-ui-server-configmap \ **Note:** Replace all JWT-related placeholder values ``, `` and `` with your environment-specific values. -**Step 4: Create the ConfigMap (`config.json`)** +**Step 4: Create the ConfigMap (`config.js`)** -Edit the values as needed for your environment. This configmap contains client settings to connect backend. +Edit the values as needed for your environment. This ConfigMap supplies the WebUI runtime config. When using Helm values instead, set `certsClientRuntime.apiUrl` (see the next section). -```bash +```javascript window.RUNTIME_CONFIG = { - API_URL: "http:///api" + API_URL: "http:///api" }; ``` +Use the URL your **browser** will call for the API (often the reverse proxy or ingress hostname, not an internal ClusterIP). + ```bash kubectl create configmap certs-ui-client-configmap \ --from-literal=config.js=' window.RUNTIME_CONFIG = { - API_URL: "http:///api" + API_URL: "http:///api" };' \ -n certs-ui ``` **Note:** -Replace `` with the actual hostname or IP address where your MaksIT.CertsUI server is configured. -This ConfigMap provides the client-side runtime configuration for the WebUI to connect to the backend API. +Replace `` with the hostname or IP users use to reach the app (including TLS and port if not 80/443). ### 3. Create a Minimal Custom Values File -Below is a minimal example of a `custom-values.yaml` for most users. It sets the storage class for persistent volumes, and configures the reverse proxy service. You can further customize this file as needed for your environment. +Below is a minimal `custom-values.yaml` aligned with the chart’s value schema in [`src/helm/values.yaml`](src/helm/values.yaml). It sets the client API URL, storage class for server PVCs, and optional registry pull secrets. ```yaml global: - imagePullSecrets: [] # Keep empty + imagePullSecrets: [] + +certsClientRuntime: + apiUrl: "https://certs-ui.example.com/api" components: server: persistence: storageClass: local-path - - reverseproxy: - service: - enabled: true - type: ClusterIP - port: 8080 - targetPort: 8080 ``` +Override **`certsServerSecrets`** and **`certsServerConfig`** here for production (JWT issuer/audience, agent hostname, ACME endpoints, and auth secrets). Chart defaults are placeholders only. + +**Services:** The chart renders one `Service` per component (`server`, `client`, `reverseproxy`). Each `service` block supports `enabled`, `type`, `port`, and `targetPort` only. For Cilium LB-IPAM, MetalLB, or cloud load balancers, use a separate manifest or your platform’s pattern so you can set annotations, `loadBalancerIP`, and session affinity; point that Service at the **reverseproxy** pods (`app.kubernetes.io/component: reverseproxy`). + ### 4. Install the Helm Chart -Install the MaksIT.CertsUI chart using your custom values file. +Install or upgrade the MaksIT.CertsUI chart using your custom values file (`helm upgrade --install` creates the release on first run). **On Linux:** ```bash -helm upgrade certs-ui oci://cr.maks-it.com/charts/certs-ui \ +helm upgrade --install certs-ui oci://cr.maks-it.com/charts/certs-ui \ -n certs-ui \ -f custom-values.yaml \ - --version 3.3.3 \ -``` - -**On Windows PowerShell:* - -```powershell -helm upgrade certs-ui oci://cr.maks-it.com/charts/certs-ui ` - -n certs-ui ` - -f custom-values.yaml ` - --version 3.3.3 ` -``` - -**Note:** -Chart version follows app version. To install a specific version, use the `--version` flag: - -### 5. Uninstall the Helm Chart - -To uninstall the MaksIT.CertsUI chart and remove all associated resources, run the following command: - -**On Linux:** - -```bash -helm uninstall certs-ui oci://cr.maks-it.com/charts/certs-ui \ - -n certs-ui + --version X.Y.Z ``` **On Windows PowerShell:** ```powershell -helm uninstall certs-ui oci://cr.maks-it.com/charts/certs-ui ` - -n certs-ui-test +helm upgrade --install certs-ui oci://cr.maks-it.com/charts/certs-ui ` + -n certs-ui ` + -f custom-values.yaml ` + --version X.Y.Z +``` + +**Note:** +`Chart.yaml` in the repository uses placeholder `version` / `appVersion` (`0.0.0`); the release pipeline sets both from the app semver when pushing the chart. When installing from your registry, pass `--version` with the chart version you published (same semver as the app release, e.g. `3.3.4`). + +### 5. Uninstall the Helm Chart + +PVCs for the server component use `helm.sh/resource-policy: keep` by default (`components.server.persistence.volumes[].pvc.keep: true`), so **`helm uninstall` does not delete them**—ACME/cache/data volumes remain until you remove the claims manually. Set `pvc.keep: false` on a volume if you want that claim deleted with the release. + +To uninstall the release (deployments, services, etc.) run: + +**On Linux:** + +```bash +helm uninstall certs-ui -n certs-ui +``` + +**On Windows PowerShell:** + +```powershell +helm uninstall certs-ui -n certs-ui ``` --- diff --git a/assets/badges/coverage-branches.svg b/assets/badges/coverage-branches.svg new file mode 100644 index 0000000..aa0aa6e --- /dev/null +++ b/assets/badges/coverage-branches.svg @@ -0,0 +1,21 @@ + + Branch Coverage: 10.6% + + + + + + + + + + + + + + + Branch Coverage + + 10.6% + + diff --git a/assets/badges/coverage-lines.svg b/assets/badges/coverage-lines.svg new file mode 100644 index 0000000..7f47a71 --- /dev/null +++ b/assets/badges/coverage-lines.svg @@ -0,0 +1,21 @@ + + Line Coverage: 21.7% + + + + + + + + + + + + + + + Line Coverage + + 21.7% + + diff --git a/assets/badges/coverage-methods.svg b/assets/badges/coverage-methods.svg new file mode 100644 index 0000000..548fa01 --- /dev/null +++ b/assets/badges/coverage-methods.svg @@ -0,0 +1,21 @@ + + Method Coverage: 29.7% + + + + + + + + + + + + + + + Method Coverage + + 29.7% + + diff --git a/src/Deploy-Helm.bat b/src/Deploy-Helm.bat deleted file mode 100644 index 6b5e5e2..0000000 --- a/src/Deploy-Helm.bat +++ /dev/null @@ -1,9 +0,0 @@ -@echo off - -REM Change directory to the location of the script -cd /d %~dp0 - -REM Invoke the PowerShell script (Release-NuGetPackage.ps1) in the same directory -powershell -ExecutionPolicy Bypass -File "%~dp0Deploy-Helm.ps1" - -pause \ No newline at end of file diff --git a/src/Deploy-Helm.ps1 b/src/Deploy-Helm.ps1 deleted file mode 100644 index 1a36a4c..0000000 --- a/src/Deploy-Helm.ps1 +++ /dev/null @@ -1,68 +0,0 @@ -# Set variables -$projectName = "certs-ui" -$namespace = "certs-ui" -$chartPath = "./helm" -$harborUrl = "cr.maks-it.com" - -# Retrieve and decode username:password from environment variable (Base64) -try { - $decoded = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Env:CR_MAKS_IT)) -} catch { - Write-Error "Failed to decode CR_MAKS_IT as Base64. Expected base64('username:password')." - exit 1 -} - -# Split decoded credentials -$creds = $decoded -split ':', 2 -$harborUsername = $creds[0] -$harborPassword = $creds[1] - -# Verify environment variable -if (-not $harborUsername -or -not $harborPassword) { - Write-Error "Decoded CR_MAKS_IT must be in the format 'username:password'." - exit 1 -} - -# Ensure namespace exists -if (-not (kubectl get ns $namespace -o name 2>$null)) { - Write-Output "Creating namespace '$namespace'..." - kubectl create namespace $namespace | Out-Null -} -else { - Write-Output "Namespace '$namespace' already exists." -} - -# Create or update Docker registry pull secret -Write-Output "Creating or updating image pull secret..." -kubectl -n $namespace create secret docker-registry cr-maksit-pull ` - --docker-server=$harborUrl ` - --docker-username=$harborUsername ` - --docker-password=$harborPassword ` - --docker-email="devnull@maks-it.com" ` - --dry-run=client -o yaml | kubectl apply -f - | Out-Null - -# Lint Helm chart -Write-Output "Linting Helm chart..." -helm lint $chartPath - -# Render Helm chart to verify output (optional) -Write-Output "Rendering Helm chart for validation..." -helm template $projectName $chartPath -n $namespace | Out-Null - -# Generate a unique rollout value (current Unix timestamp) -$rollme = [int][double]::Parse((Get-Date -UFormat %s)) - -# Deploy Helm release -Write-Output "Deploying Helm release '$projectName'..." -helm upgrade --install $projectName $chartPath -n $namespace ` - --set imagePullSecret.create=false ` - --set imagePullSecrets[0].name=cr-maksit-pull ` - --set-string "rollme=$rollme" - -# Check deployment status -Write-Output "Waiting for deployment rollout..." -kubectl -n $namespace rollout status deployment/$projectName-reverseproxy - -# Display service details -Write-Output "Service information:" -kubectl -n $namespace get svc $projectName-reverseproxy diff --git a/src/LetsEncrypt.Tests/ContentTypeTests.cs b/src/LetsEncrypt.Tests/ContentTypeTests.cs new file mode 100644 index 0000000..e84b470 --- /dev/null +++ b/src/LetsEncrypt.Tests/ContentTypeTests.cs @@ -0,0 +1,13 @@ +using MaksIT.LetsEncrypt.Entities; +using Xunit; + +namespace MaksIT.LetsEncrypt.Tests; + +public class ContentTypeTests +{ + [Fact] + public void ContentType_defines_expected_values() + { + Assert.Equal(4, Enum.GetValues().Length); + } +} diff --git a/src/LetsEncrypt.Tests/LetsEncrypt.Tests.csproj b/src/LetsEncrypt.Tests/LetsEncrypt.Tests.csproj new file mode 100644 index 0000000..83b8a10 --- /dev/null +++ b/src/LetsEncrypt.Tests/LetsEncrypt.Tests.csproj @@ -0,0 +1,28 @@ + + + + net10.0 + enable + enable + false + MaksIT.LetsEncrypt.Tests + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/src/LetsEncrypt/LetsEncrypt.csproj b/src/LetsEncrypt/LetsEncrypt.csproj index 5307ac9..c1cc889 100644 --- a/src/LetsEncrypt/LetsEncrypt.csproj +++ b/src/LetsEncrypt/LetsEncrypt.csproj @@ -1,12 +1,10 @@ - + net10.0 enable enable MaksIT.$(MSBuildProjectName.Replace(" ", "_")) - README.md - LICENSE.md diff --git a/src/MaksIT.CertsUI.sln b/src/MaksIT.CertsUI.sln index 0fd4905..8c1e257 100644 --- a/src/MaksIT.CertsUI.sln +++ b/src/MaksIT.CertsUI.sln @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LetsEncrypt", "LetsEncrypt\ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentation", "{3374FDB1-C95E-4103-8E14-5BBF0BDC4E9D}" ProjectSection(SolutionItems) = preProject + ..\CHANGELOG.md = ..\CHANGELOG.md + ..\CONTRIBUTING.md = ..\CONTRIBUTING.md ..\LICENSE.md = ..\LICENSE.md ..\README.md = ..\README.md EndProjectSection @@ -19,32 +21,100 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MaksIT.Models", "MaksIT.Mod EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReverseProxy", "ReverseProxy\ReverseProxy.csproj", "{BE051147-7AB7-4358-9C24-5CB40FAFF4FC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LetsEncrypt.Tests", "LetsEncrypt.Tests\LetsEncrypt.Tests.csproj", "{65DEB577-FB14-488C-AEB1-3172F0812950}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaksIT.Webapi.Tests", "MaksIT.Webapi.Tests\MaksIT.Webapi.Tests.csproj", "{EB57DB1C-DEEE-4952-9326-FB09903651F4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7DE431E5-889C-434E-AD02-9F89D7A0ED27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7DE431E5-889C-434E-AD02-9F89D7A0ED27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DE431E5-889C-434E-AD02-9F89D7A0ED27}.Debug|x64.ActiveCfg = Debug|Any CPU + {7DE431E5-889C-434E-AD02-9F89D7A0ED27}.Debug|x64.Build.0 = Debug|Any CPU + {7DE431E5-889C-434E-AD02-9F89D7A0ED27}.Debug|x86.ActiveCfg = Debug|Any CPU + {7DE431E5-889C-434E-AD02-9F89D7A0ED27}.Debug|x86.Build.0 = Debug|Any CPU {7DE431E5-889C-434E-AD02-9F89D7A0ED27}.Release|Any CPU.ActiveCfg = Release|Any CPU {7DE431E5-889C-434E-AD02-9F89D7A0ED27}.Release|Any CPU.Build.0 = Release|Any CPU + {7DE431E5-889C-434E-AD02-9F89D7A0ED27}.Release|x64.ActiveCfg = Release|Any CPU + {7DE431E5-889C-434E-AD02-9F89D7A0ED27}.Release|x64.Build.0 = Release|Any CPU + {7DE431E5-889C-434E-AD02-9F89D7A0ED27}.Release|x86.ActiveCfg = Release|Any CPU + {7DE431E5-889C-434E-AD02-9F89D7A0ED27}.Release|x86.Build.0 = Release|Any CPU {B5F39E04-C2E3-49BF-82C2-9DEBAA949E3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B5F39E04-C2E3-49BF-82C2-9DEBAA949E3D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5F39E04-C2E3-49BF-82C2-9DEBAA949E3D}.Debug|x64.ActiveCfg = Debug|Any CPU + {B5F39E04-C2E3-49BF-82C2-9DEBAA949E3D}.Debug|x64.Build.0 = Debug|Any CPU + {B5F39E04-C2E3-49BF-82C2-9DEBAA949E3D}.Debug|x86.ActiveCfg = Debug|Any CPU + {B5F39E04-C2E3-49BF-82C2-9DEBAA949E3D}.Debug|x86.Build.0 = Debug|Any CPU {B5F39E04-C2E3-49BF-82C2-9DEBAA949E3D}.Release|Any CPU.ActiveCfg = Release|Any CPU {B5F39E04-C2E3-49BF-82C2-9DEBAA949E3D}.Release|Any CPU.Build.0 = Release|Any CPU + {B5F39E04-C2E3-49BF-82C2-9DEBAA949E3D}.Release|x64.ActiveCfg = Release|Any CPU + {B5F39E04-C2E3-49BF-82C2-9DEBAA949E3D}.Release|x64.Build.0 = Release|Any CPU + {B5F39E04-C2E3-49BF-82C2-9DEBAA949E3D}.Release|x86.ActiveCfg = Release|Any CPU + {B5F39E04-C2E3-49BF-82C2-9DEBAA949E3D}.Release|x86.Build.0 = Release|Any CPU {0233E43F-435D-4309-B20C-ECD4BFBD2E63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0233E43F-435D-4309-B20C-ECD4BFBD2E63}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0233E43F-435D-4309-B20C-ECD4BFBD2E63}.Debug|x64.ActiveCfg = Debug|x64 + {0233E43F-435D-4309-B20C-ECD4BFBD2E63}.Debug|x86.ActiveCfg = Debug|x86 {0233E43F-435D-4309-B20C-ECD4BFBD2E63}.Release|Any CPU.ActiveCfg = Release|Any CPU {0233E43F-435D-4309-B20C-ECD4BFBD2E63}.Release|Any CPU.Build.0 = Release|Any CPU + {0233E43F-435D-4309-B20C-ECD4BFBD2E63}.Release|x64.ActiveCfg = Release|x64 + {0233E43F-435D-4309-B20C-ECD4BFBD2E63}.Release|x86.ActiveCfg = Release|x86 {6814169B-D4D0-40B2-9FA9-89997DD44C30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6814169B-D4D0-40B2-9FA9-89997DD44C30}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6814169B-D4D0-40B2-9FA9-89997DD44C30}.Debug|x64.ActiveCfg = Debug|Any CPU + {6814169B-D4D0-40B2-9FA9-89997DD44C30}.Debug|x64.Build.0 = Debug|Any CPU + {6814169B-D4D0-40B2-9FA9-89997DD44C30}.Debug|x86.ActiveCfg = Debug|Any CPU + {6814169B-D4D0-40B2-9FA9-89997DD44C30}.Debug|x86.Build.0 = Debug|Any CPU {6814169B-D4D0-40B2-9FA9-89997DD44C30}.Release|Any CPU.ActiveCfg = Release|Any CPU {6814169B-D4D0-40B2-9FA9-89997DD44C30}.Release|Any CPU.Build.0 = Release|Any CPU + {6814169B-D4D0-40B2-9FA9-89997DD44C30}.Release|x64.ActiveCfg = Release|Any CPU + {6814169B-D4D0-40B2-9FA9-89997DD44C30}.Release|x64.Build.0 = Release|Any CPU + {6814169B-D4D0-40B2-9FA9-89997DD44C30}.Release|x86.ActiveCfg = Release|Any CPU + {6814169B-D4D0-40B2-9FA9-89997DD44C30}.Release|x86.Build.0 = Release|Any CPU {BE051147-7AB7-4358-9C24-5CB40FAFF4FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BE051147-7AB7-4358-9C24-5CB40FAFF4FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE051147-7AB7-4358-9C24-5CB40FAFF4FC}.Debug|x64.ActiveCfg = Debug|Any CPU + {BE051147-7AB7-4358-9C24-5CB40FAFF4FC}.Debug|x64.Build.0 = Debug|Any CPU + {BE051147-7AB7-4358-9C24-5CB40FAFF4FC}.Debug|x86.ActiveCfg = Debug|Any CPU + {BE051147-7AB7-4358-9C24-5CB40FAFF4FC}.Debug|x86.Build.0 = Debug|Any CPU {BE051147-7AB7-4358-9C24-5CB40FAFF4FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {BE051147-7AB7-4358-9C24-5CB40FAFF4FC}.Release|Any CPU.Build.0 = Release|Any CPU + {BE051147-7AB7-4358-9C24-5CB40FAFF4FC}.Release|x64.ActiveCfg = Release|Any CPU + {BE051147-7AB7-4358-9C24-5CB40FAFF4FC}.Release|x64.Build.0 = Release|Any CPU + {BE051147-7AB7-4358-9C24-5CB40FAFF4FC}.Release|x86.ActiveCfg = Release|Any CPU + {BE051147-7AB7-4358-9C24-5CB40FAFF4FC}.Release|x86.Build.0 = Release|Any CPU + {65DEB577-FB14-488C-AEB1-3172F0812950}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65DEB577-FB14-488C-AEB1-3172F0812950}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65DEB577-FB14-488C-AEB1-3172F0812950}.Debug|x64.ActiveCfg = Debug|Any CPU + {65DEB577-FB14-488C-AEB1-3172F0812950}.Debug|x64.Build.0 = Debug|Any CPU + {65DEB577-FB14-488C-AEB1-3172F0812950}.Debug|x86.ActiveCfg = Debug|Any CPU + {65DEB577-FB14-488C-AEB1-3172F0812950}.Debug|x86.Build.0 = Debug|Any CPU + {65DEB577-FB14-488C-AEB1-3172F0812950}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65DEB577-FB14-488C-AEB1-3172F0812950}.Release|Any CPU.Build.0 = Release|Any CPU + {65DEB577-FB14-488C-AEB1-3172F0812950}.Release|x64.ActiveCfg = Release|Any CPU + {65DEB577-FB14-488C-AEB1-3172F0812950}.Release|x64.Build.0 = Release|Any CPU + {65DEB577-FB14-488C-AEB1-3172F0812950}.Release|x86.ActiveCfg = Release|Any CPU + {65DEB577-FB14-488C-AEB1-3172F0812950}.Release|x86.Build.0 = Release|Any CPU + {EB57DB1C-DEEE-4952-9326-FB09903651F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB57DB1C-DEEE-4952-9326-FB09903651F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB57DB1C-DEEE-4952-9326-FB09903651F4}.Debug|x64.ActiveCfg = Debug|Any CPU + {EB57DB1C-DEEE-4952-9326-FB09903651F4}.Debug|x64.Build.0 = Debug|Any CPU + {EB57DB1C-DEEE-4952-9326-FB09903651F4}.Debug|x86.ActiveCfg = Debug|Any CPU + {EB57DB1C-DEEE-4952-9326-FB09903651F4}.Debug|x86.Build.0 = Debug|Any CPU + {EB57DB1C-DEEE-4952-9326-FB09903651F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB57DB1C-DEEE-4952-9326-FB09903651F4}.Release|Any CPU.Build.0 = Release|Any CPU + {EB57DB1C-DEEE-4952-9326-FB09903651F4}.Release|x64.ActiveCfg = Release|Any CPU + {EB57DB1C-DEEE-4952-9326-FB09903651F4}.Release|x64.Build.0 = Release|Any CPU + {EB57DB1C-DEEE-4952-9326-FB09903651F4}.Release|x86.ActiveCfg = Release|Any CPU + {EB57DB1C-DEEE-4952-9326-FB09903651F4}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/MaksIT.WebUI/New Text Document.txt b/src/MaksIT.WebUI/New Text Document.txt deleted file mode 100644 index 4a1421c..0000000 --- a/src/MaksIT.WebUI/New Text Document.txt +++ /dev/null @@ -1,25 +0,0 @@ -npm create vite@latest my-react-app - -npm install -D tailwindcss postcss autoprefixer -npx tailwindcss init -p - -tailwind.config.js - -/** @type {import('tailwindcss').Config} */ -export default { - content: [ - "./index.html", - "./src/**/*.{js,ts,jsx,tsx}", - ], - theme: { - extend: {}, - }, - plugins: [], -} - - -index.css - -@tailwind base; -@tailwind components; -@tailwind utilities; \ No newline at end of file diff --git a/src/MaksIT.WebUI/package-lock.json b/src/MaksIT.WebUI/package-lock.json index f9d72bc..de9661e 100644 --- a/src/MaksIT.WebUI/package-lock.json +++ b/src/MaksIT.WebUI/package-lock.json @@ -19,7 +19,6 @@ "qrcode.react": "^4.2.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "react-pdf": "^10.2.0", "react-redux": "^9.2.0", "react-router-dom": "^7.9.6", "react-virtualized": "^9.22.6", @@ -999,191 +998,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@napi-rs/canvas": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.81.tgz", - "integrity": "sha512-ReCjd5SYI/UKx/olaQLC4GtN6wUQGjlgHXs1lvUvWGXfBMR3Fxnik3cL+OxKN5ithNdoU0/GlCrdKcQDFh2XKQ==", - "license": "MIT", - "optional": true, - "workspaces": [ - "e2e/*" - ], - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@napi-rs/canvas-android-arm64": "0.1.81", - "@napi-rs/canvas-darwin-arm64": "0.1.81", - "@napi-rs/canvas-darwin-x64": "0.1.81", - "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.81", - "@napi-rs/canvas-linux-arm64-gnu": "0.1.81", - "@napi-rs/canvas-linux-arm64-musl": "0.1.81", - "@napi-rs/canvas-linux-riscv64-gnu": "0.1.81", - "@napi-rs/canvas-linux-x64-gnu": "0.1.81", - "@napi-rs/canvas-linux-x64-musl": "0.1.81", - "@napi-rs/canvas-win32-x64-msvc": "0.1.81" - } - }, - "node_modules/@napi-rs/canvas-android-arm64": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.81.tgz", - "integrity": "sha512-78Lz+AUi+MsWupyZjXwpwQrp1QCwncPvRZrdvrROcZ9Gq9grP7LfQZiGdR8LKyHIq3OR18mDP+JESGT15V1nXw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-darwin-arm64": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.81.tgz", - "integrity": "sha512-omejuKgHWKDGoh8rsgsyhm/whwxMaryTQjJTd9zD7hiB9/rzcEEJLHnzXWR5ysy4/tTjHaQotE6k2t8eodTLnA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-darwin-x64": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.81.tgz", - "integrity": "sha512-EYfk+co6BElq5DXNH9PBLYDYwc4QsvIVbyrsVHsxVpn4p6Y3/s8MChgC69AGqj3vzZBQ1qx2CRCMtg5cub+XuQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.81.tgz", - "integrity": "sha512-teh6Q74CyAcH31yLNQGR9MtXSFxlZa5CI6vvNUISI14gWIJWrhOwUAOly+KRe1aztWR0FWTVSPxM4p5y+06aow==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-linux-arm64-gnu": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.81.tgz", - "integrity": "sha512-AGEopHFYRzJOjxY+2G1RmHPRnuWvO3Qdhq7sIazlSjxb3Z6dZHg7OB/4ZimXaimPjDACm9qWa6t5bn9bhXvkcw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-linux-arm64-musl": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.81.tgz", - "integrity": "sha512-Bj3m1cl4GIhsigkdwOxii4g4Ump3/QhNpx85IgAlCCYXpaly6mcsWpuDYEabfIGWOWhDUNBOndaQUPfWK1czOQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.81.tgz", - "integrity": "sha512-yg/5NkHykVdwPlD3XObwCa/EswkOwLHswJcI9rHrac+znHsmCSj5AMX/RTU9Z9F6lZTwL60JM2Esit33XhAMiw==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-linux-x64-gnu": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.81.tgz", - "integrity": "sha512-tPfMpSEBuV5dJSKexO/UZxpOqnYTaNbG8aKa1ek8QsWu+4SJ/foWkaxscra/RUv85vepx6WWDjzBNbNJsTnO0w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-linux-x64-musl": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.81.tgz", - "integrity": "sha512-1L0xnYgzqn8Baef+inPvY4dKqdmw3KCBoe0NEDgezuBZN7MA5xElwifoG8609uNdrMtJ9J6QZarsslLRVqri7g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-win32-x64-msvc": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.81.tgz", - "integrity": "sha512-57ryVbhm/z7RE9/UVcS7mrLPdlayLesy+9U0Uf6epCoeSGrs99tfieCcgZWFbIgmByQ1AZnNtFI2N6huqDLlWQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3181,15 +2995,6 @@ "node": ">=0.4.0" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/detect-libc": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", @@ -5112,24 +4917,6 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/make-cancellable-promise": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz", - "integrity": "sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw==", - "license": "MIT", - "funding": { - "url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1" - } - }, - "node_modules/make-event-props": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-2.0.0.tgz", - "integrity": "sha512-G/hncXrl4Qt7mauJEXSg3AcdYzmpkIITTNl5I+rH9sog5Yw0kK6vseJjCaPfOXqOqQuPUP89Rkhfz5kPS8ijtw==", - "license": "MIT", - "funding": { - "url": "https://github.com/wojtekmaj/make-event-props?sponsor=1" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -5139,23 +4926,6 @@ "node": ">= 0.4" } }, - "node_modules/merge-refs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz", - "integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==", - "license": "MIT", - "funding": { - "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5479,18 +5249,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pdfjs-dist": { - "version": "5.4.296", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.296.tgz", - "integrity": "sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==", - "license": "Apache-2.0", - "engines": { - "node": ">=20.16.0 || >=22.3.0" - }, - "optionalDependencies": { - "@napi-rs/canvas": "^0.1.80" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5666,44 +5424,6 @@ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", "license": "MIT" }, - "node_modules/react-pdf": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-10.2.0.tgz", - "integrity": "sha512-zk0DIL31oCh8cuQycM0SJKfwh4Onz0/Nwi6wTOjgtEjWGUY6eM+/vuzvOP3j70qtEULn7m1JtaeGzud1w5fY2Q==", - "license": "MIT", - "dependencies": { - "clsx": "^2.0.0", - "dequal": "^2.0.3", - "make-cancellable-promise": "^2.0.0", - "make-event-props": "^2.0.0", - "merge-refs": "^2.0.0", - "pdfjs-dist": "5.4.296", - "tiny-invariant": "^1.0.0", - "warning": "^4.0.0" - }, - "funding": { - "url": "https://github.com/wojtekmaj/react-pdf?sponsor=1" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-pdf/node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", @@ -6349,12 +6069,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -6757,15 +6471,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/src/MaksIT.WebUI/package.json b/src/MaksIT.WebUI/package.json index ef0c8fa..0dc6776 100644 --- a/src/MaksIT.WebUI/package.json +++ b/src/MaksIT.WebUI/package.json @@ -21,7 +21,6 @@ "qrcode.react": "^4.2.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "react-pdf": "^10.2.0", "react-redux": "^9.2.0", "react-router-dom": "^7.9.6", "react-virtualized": "^9.22.6", diff --git a/src/MaksIT.WebUI/src/components/FormLayout/FormContent.tsx b/src/MaksIT.WebUI/src/components/FormLayout/FormContent.tsx index 6feeef0..2860dca 100644 --- a/src/MaksIT.WebUI/src/components/FormLayout/FormContent.tsx +++ b/src/MaksIT.WebUI/src/components/FormLayout/FormContent.tsx @@ -2,14 +2,18 @@ import { FC, ReactNode } from 'react' interface FormContentProps { children?: ReactNode + /** Merged after base layout; use e.g. `flex flex-col overflow-hidden` when a child should fill height (iframe). */ + className?: string } const FormContent: FC = (props) => { const { - children + children, + className } = props - return
+ const base = 'bg-gray-100 w-full h-full min-h-0 p-4' + return
{children}
} diff --git a/src/MaksIT.WebUI/src/forms/LetsEncryptTermsOfService.tsx b/src/MaksIT.WebUI/src/forms/LetsEncryptTermsOfService.tsx index c99c64b..a588d1a 100644 --- a/src/MaksIT.WebUI/src/forms/LetsEncryptTermsOfService.tsx +++ b/src/MaksIT.WebUI/src/forms/LetsEncryptTermsOfService.tsx @@ -1,43 +1,18 @@ -import { FC, useEffect, useRef, useState } from 'react' +import { FC, useEffect, useState } from 'react' import { FormContainer, FormContent, FormFooter, FormHeader } from '../components/FormLayout' import { ApiRoutes, GetApiRoute } from '../AppMap' import { getData, postData } from '../axiosConfig' -import { pdfjs, Document, Page } from 'react-pdf' -import 'react-pdf/dist/Page/AnnotationLayer.css' -import 'react-pdf/dist/Page/TextLayer.css' - -import type { PDFDocumentProxy } from 'pdfjs-dist' -import pdfWorkerUrl from 'pdfjs-dist/build/pdf.worker.min.mjs?url' - -// pdfjs-dist worker (bundled asset URL for prod) -pdfjs.GlobalWorkerOptions.workerSrc = pdfWorkerUrl - +/** + * Uses the browser's native PDF viewer (iframe + blob URL). No PDF.js / workers / extra MIME rules. + * UX varies slightly by browser; mobile Safari may open PDF in a new context instead of inline. + */ const LetsEncryptTermsOfService: FC = () => { - const [pdfUrl, setPdfUrl] = useState(null) const [objectUrl, setObjectUrl] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) - const [numPages, setNumPages] = useState() - const containerRef = useRef(null) - const [containerWidth, setContainerWidth] = useState() - - useEffect(() => { - const handleResize = () => { - if (containerRef.current) { - const { x } = containerRef.current.getBoundingClientRect() - const width = window.innerWidth - x - setContainerWidth(width) - } - } - handleResize() - window.addEventListener('resize', handleResize) - return () => { - window.removeEventListener('resize', handleResize) - } - }, []) useEffect(() => { setLoading(true) @@ -46,10 +21,13 @@ const LetsEncryptTermsOfService: FC = () => { }) .then(response => { if (!response) return - return getData(GetApiRoute(ApiRoutes.CERTS_FLOW_TERMS_OF_SERVICE).route.replace('{sessionId}', response)) + return getData( + GetApiRoute(ApiRoutes.CERTS_FLOW_TERMS_OF_SERVICE).route.replace('{sessionId}', response), + 120_000 + ) }) .then(base64Pdf => { - if (base64Pdf) { + if (typeof base64Pdf === 'string' && base64Pdf.length > 0) { setPdfUrl(base64Pdf) } else { setError('Failed to retrieve PDF.') @@ -59,12 +37,16 @@ const LetsEncryptTermsOfService: FC = () => { .finally(() => setLoading(false)) }, []) - // Convert base64 to Blob and create object URL useEffect(() => { if (!pdfUrl) return - // Remove data URL prefix if present - const base64 = pdfUrl.replace(/^data:application\/pdf;base64,/, '') - const byteCharacters = atob(base64) + const base64 = pdfUrl.replace(/^data:application\/pdf;base64,/, '').replace(/\s/g, '') + let byteCharacters: string + try { + byteCharacters = atob(base64) + } catch { + setError('Invalid PDF data from server.') + return + } const byteNumbers = new Array(byteCharacters.length) for (let i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i) @@ -78,42 +60,28 @@ const LetsEncryptTermsOfService: FC = () => { } }, [pdfUrl]) - const handleDocumentLoadSuccess = ({ numPages: nextNumPages }: PDFDocumentProxy): void => { - setNumPages(nextNumPages) - } - return ( Let's Encrypt Terms of Service - - {loading &&
Loading Terms of Service...
} - {error &&
{error}
} - {objectUrl && ( -
- - {numPages ? ( - Array.from(new Array(numPages), (_, index) => ( -
- 0 ? containerWidth : 600} - /> -
- Page {index + 1} / {numPages} -
-
- )) - ) : ( -
Loading PDF pages...
- )} -
-
- )} + +
+ {loading &&
Loading Terms of Service...
} + {error &&
{error}
} + {objectUrl && !error && ( +