From 36b3c4f2b6a62acdc34a9b586c7e0bd53946a5f8 Mon Sep 17 00:00:00 2001 From: 6d486f49 Date: Sat, 26 Sep 2026 00:48:37 -0400 Subject: [PATCH] Updating structure --- .../rename-projects-to-zerospace-format.md | 117 ++++++++ ZS/Web/Program.cs | 13 - ZS/ZS.sln | 34 --- ZeroSpace/.dockerignore | 9 + {ZS => ZeroSpace}/.junie/memory/errors.md | 0 {ZS => ZeroSpace}/.junie/memory/feedback.md | 0 {ZS => ZeroSpace}/.junie/memory/language.json | 0 .../.junie/memory/memory.version | 0 {ZS => ZeroSpace}/.junie/memory/tasks.md | 0 .../.junie/plans/add-aspire-k8s-postgres.md | 199 +++++++++++++ .../Data/DbInitializer.cs | 31 ++ .../Data/ZeroSpaceDbContext.cs | 66 +++++ ZeroSpace/ZeroSpace.ApiService/Dockerfile | 21 ++ .../Endpoints/UnitsEndpoints.cs | 93 ++++++ .../ZeroSpace.ApiService/GraphQL/Mutation.cs | 136 +++++++++ .../ZeroSpace.ApiService/GraphQL/Query.cs | 19 ++ ZeroSpace/ZeroSpace.ApiService/Program.cs | 64 ++++ .../Properties/launchSettings.json | 23 ++ .../ZeroSpace.ApiService.csproj | 21 ++ .../appsettings.Development.json | 8 + .../ZeroSpace.ApiService/appsettings.json | 9 + ZeroSpace/ZeroSpace.AppHost/Program.cs | 15 + .../Properties/launchSettings.json | 29 ++ .../ZeroSpace.AppHost.csproj | 24 ++ .../appsettings.Development.json | 10 + ZeroSpace/ZeroSpace.AppHost/appsettings.json | 9 + .../ZeroSpace.Build}/Program.cs | 32 +- .../ZeroSpace.Build/ZeroSpace.Build.csproj | 0 .../ZeroSpace.Deploy}/Program.cs | 4 +- .../ZeroSpace.Deploy/ZeroSpace.Deploy.csproj | 0 .../ZeroSpace.Model}/UnitData.cs | 2 +- .../ZeroSpace.Model}/UnitEfficiency.cs | 2 +- .../ZeroSpace.Model}/Units.g.cs | 68 +---- .../ZeroSpace.Model/ZeroSpace.Model.csproj | 0 .../ZeroSpace.ServiceDefaults/Extensions.cs | 108 +++++++ .../ZeroSpace.ServiceDefaults.csproj | 22 ++ {ZS/Web => ZeroSpace/ZeroSpace.Web}/App.razor | 0 ZeroSpace/ZeroSpace.Web/Dockerfile | 20 ++ .../ZeroSpace.Web}/Layout/MainLayout.razor | 0 .../Layout/MainLayout.razor.css | 0 .../ZeroSpace.Web}/Layout/NavMenu.razor | 0 .../ZeroSpace.Web}/Layout/NavMenu.razor.css | 0 .../ZeroSpace.Web}/Pages/Home.razor | 0 .../ZeroSpace.Web}/Pages/NotFound.razor | 0 .../ZeroSpace.Web}/Pages/Units.razor | 49 ++- ZeroSpace/ZeroSpace.Web/Program.cs | 29 ++ .../Properties/launchSettings.json | 0 .../ZeroSpace.Web/Services/IUnitsApiClient.cs | 11 + .../ZeroSpace.Web/Services/UnitsApiClient.cs | 278 ++++++++++++++++++ .../ZeroSpace.Web/ZeroSpace.Web.csproj | 3 +- .../ZeroSpace.Web}/_Imports.razor | 5 +- ZeroSpace/ZeroSpace.Web/nginx.conf | 27 ++ .../wwwroot/appsettings.Development.json | 3 + .../ZeroSpace.Web/wwwroot/appsettings.json | 3 + .../ZeroSpace.Web}/wwwroot/css/app.css | 0 .../ZeroSpace.Web}/wwwroot/favicon.png | Bin .../ZeroSpace.Web}/wwwroot/icon-192.png | Bin .../ZeroSpace.Web}/wwwroot/index.html | 2 +- .../lib/bootstrap/dist/css/bootstrap-grid.css | 0 .../bootstrap/dist/css/bootstrap-grid.css.map | 0 .../bootstrap/dist/css/bootstrap-grid.min.css | 0 .../dist/css/bootstrap-grid.min.css.map | 0 .../bootstrap/dist/css/bootstrap-grid.rtl.css | 0 .../dist/css/bootstrap-grid.rtl.css.map | 0 .../dist/css/bootstrap-grid.rtl.min.css | 0 .../dist/css/bootstrap-grid.rtl.min.css.map | 0 .../bootstrap/dist/css/bootstrap-reboot.css | 0 .../dist/css/bootstrap-reboot.css.map | 0 .../dist/css/bootstrap-reboot.min.css | 0 .../dist/css/bootstrap-reboot.min.css.map | 0 .../dist/css/bootstrap-reboot.rtl.css | 0 .../dist/css/bootstrap-reboot.rtl.css.map | 0 .../dist/css/bootstrap-reboot.rtl.min.css | 0 .../dist/css/bootstrap-reboot.rtl.min.css.map | 0 .../dist/css/bootstrap-utilities.css | 0 .../dist/css/bootstrap-utilities.css.map | 0 .../dist/css/bootstrap-utilities.min.css | 0 .../dist/css/bootstrap-utilities.min.css.map | 0 .../dist/css/bootstrap-utilities.rtl.css | 0 .../dist/css/bootstrap-utilities.rtl.css.map | 0 .../dist/css/bootstrap-utilities.rtl.min.css | 0 .../css/bootstrap-utilities.rtl.min.css.map | 0 .../lib/bootstrap/dist/css/bootstrap.css | 0 .../lib/bootstrap/dist/css/bootstrap.css.map | 0 .../lib/bootstrap/dist/css/bootstrap.min.css | 0 .../bootstrap/dist/css/bootstrap.min.css.map | 0 .../lib/bootstrap/dist/css/bootstrap.rtl.css | 0 .../bootstrap/dist/css/bootstrap.rtl.css.map | 0 .../bootstrap/dist/css/bootstrap.rtl.min.css | 0 .../dist/css/bootstrap.rtl.min.css.map | 0 .../lib/bootstrap/dist/js/bootstrap.bundle.js | 0 .../bootstrap/dist/js/bootstrap.bundle.js.map | 0 .../bootstrap/dist/js/bootstrap.bundle.min.js | 0 .../dist/js/bootstrap.bundle.min.js.map | 0 .../lib/bootstrap/dist/js/bootstrap.esm.js | 0 .../bootstrap/dist/js/bootstrap.esm.js.map | 0 .../bootstrap/dist/js/bootstrap.esm.min.js | 0 .../dist/js/bootstrap.esm.min.js.map | 0 .../lib/bootstrap/dist/js/bootstrap.js | 0 .../lib/bootstrap/dist/js/bootstrap.js.map | 0 .../lib/bootstrap/dist/js/bootstrap.min.js | 0 .../bootstrap/dist/js/bootstrap.min.js.map | 0 .../wwwroot/sample-data/weather.json | 0 ZeroSpace/ZeroSpace.sln | 115 ++++++++ ZeroSpace/aspirate.json | 14 + ZeroSpace/docker-compose.yml | 49 +++ ZeroSpace/k8s/apiservice-deployment.yaml | 59 ++++ ZeroSpace/k8s/apiservice-service.yaml | 15 + ZeroSpace/k8s/ingress.yaml | 35 +++ ZeroSpace/k8s/kustomization.yaml | 16 + ZeroSpace/k8s/postgres-deployment.yaml | 63 ++++ ZeroSpace/k8s/postgres-pvc.yaml | 13 + ZeroSpace/k8s/postgres-secret.yaml | 13 + ZeroSpace/k8s/postgres-service.yaml | 15 + ZeroSpace/k8s/web-deployment.yaml | 47 +++ ZeroSpace/k8s/web-service.yaml | 15 + 116 files changed, 1947 insertions(+), 140 deletions(-) create mode 100644 .junie/plans/rename-projects-to-zerospace-format.md delete mode 100644 ZS/Web/Program.cs delete mode 100644 ZS/ZS.sln create mode 100644 ZeroSpace/.dockerignore rename {ZS => ZeroSpace}/.junie/memory/errors.md (100%) rename {ZS => ZeroSpace}/.junie/memory/feedback.md (100%) rename {ZS => ZeroSpace}/.junie/memory/language.json (100%) rename {ZS => ZeroSpace}/.junie/memory/memory.version (100%) rename {ZS => ZeroSpace}/.junie/memory/tasks.md (100%) create mode 100644 ZeroSpace/.junie/plans/add-aspire-k8s-postgres.md create mode 100644 ZeroSpace/ZeroSpace.ApiService/Data/DbInitializer.cs create mode 100644 ZeroSpace/ZeroSpace.ApiService/Data/ZeroSpaceDbContext.cs create mode 100644 ZeroSpace/ZeroSpace.ApiService/Dockerfile create mode 100644 ZeroSpace/ZeroSpace.ApiService/Endpoints/UnitsEndpoints.cs create mode 100644 ZeroSpace/ZeroSpace.ApiService/GraphQL/Mutation.cs create mode 100644 ZeroSpace/ZeroSpace.ApiService/GraphQL/Query.cs create mode 100644 ZeroSpace/ZeroSpace.ApiService/Program.cs create mode 100644 ZeroSpace/ZeroSpace.ApiService/Properties/launchSettings.json create mode 100644 ZeroSpace/ZeroSpace.ApiService/ZeroSpace.ApiService.csproj create mode 100644 ZeroSpace/ZeroSpace.ApiService/appsettings.Development.json create mode 100644 ZeroSpace/ZeroSpace.ApiService/appsettings.json create mode 100644 ZeroSpace/ZeroSpace.AppHost/Program.cs create mode 100644 ZeroSpace/ZeroSpace.AppHost/Properties/launchSettings.json create mode 100644 ZeroSpace/ZeroSpace.AppHost/ZeroSpace.AppHost.csproj create mode 100644 ZeroSpace/ZeroSpace.AppHost/appsettings.Development.json create mode 100644 ZeroSpace/ZeroSpace.AppHost/appsettings.json rename {ZS/Build => ZeroSpace/ZeroSpace.Build}/Program.cs (92%) rename ZS/Build/Build.csproj => ZeroSpace/ZeroSpace.Build/ZeroSpace.Build.csproj (100%) rename {ZS/Deploy => ZeroSpace/ZeroSpace.Deploy}/Program.cs (89%) rename ZS/Deploy/Deploy.csproj => ZeroSpace/ZeroSpace.Deploy/ZeroSpace.Deploy.csproj (100%) rename {ZS/Model => ZeroSpace/ZeroSpace.Model}/UnitData.cs (97%) rename {ZS/Model => ZeroSpace/ZeroSpace.Model}/UnitEfficiency.cs (97%) rename {ZS/Model => ZeroSpace/ZeroSpace.Model}/Units.g.cs (81%) rename ZS/Model/Model.csproj => ZeroSpace/ZeroSpace.Model/ZeroSpace.Model.csproj (100%) create mode 100644 ZeroSpace/ZeroSpace.ServiceDefaults/Extensions.cs create mode 100644 ZeroSpace/ZeroSpace.ServiceDefaults/ZeroSpace.ServiceDefaults.csproj rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/App.razor (100%) create mode 100644 ZeroSpace/ZeroSpace.Web/Dockerfile rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/Layout/MainLayout.razor (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/Layout/MainLayout.razor.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/Layout/NavMenu.razor (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/Layout/NavMenu.razor.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/Pages/Home.razor (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/Pages/NotFound.razor (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/Pages/Units.razor (70%) create mode 100644 ZeroSpace/ZeroSpace.Web/Program.cs rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/Properties/launchSettings.json (100%) create mode 100644 ZeroSpace/ZeroSpace.Web/Services/IUnitsApiClient.cs create mode 100644 ZeroSpace/ZeroSpace.Web/Services/UnitsApiClient.cs rename ZS/Web/Web.csproj => ZeroSpace/ZeroSpace.Web/ZeroSpace.Web.csproj (81%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/_Imports.razor (82%) create mode 100644 ZeroSpace/ZeroSpace.Web/nginx.conf create mode 100644 ZeroSpace/ZeroSpace.Web/wwwroot/appsettings.Development.json create mode 100644 ZeroSpace/ZeroSpace.Web/wwwroot/appsettings.json rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/css/app.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/favicon.png (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/icon-192.png (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/index.html (94%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/js/bootstrap.js (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map (100%) rename {ZS/Web => ZeroSpace/ZeroSpace.Web}/wwwroot/sample-data/weather.json (100%) create mode 100644 ZeroSpace/ZeroSpace.sln create mode 100644 ZeroSpace/aspirate.json create mode 100644 ZeroSpace/docker-compose.yml create mode 100644 ZeroSpace/k8s/apiservice-deployment.yaml create mode 100644 ZeroSpace/k8s/apiservice-service.yaml create mode 100644 ZeroSpace/k8s/ingress.yaml create mode 100644 ZeroSpace/k8s/kustomization.yaml create mode 100644 ZeroSpace/k8s/postgres-deployment.yaml create mode 100644 ZeroSpace/k8s/postgres-pvc.yaml create mode 100644 ZeroSpace/k8s/postgres-secret.yaml create mode 100644 ZeroSpace/k8s/postgres-service.yaml create mode 100644 ZeroSpace/k8s/web-deployment.yaml create mode 100644 ZeroSpace/k8s/web-service.yaml diff --git a/.junie/plans/rename-projects-to-zerospace-format.md b/.junie/plans/rename-projects-to-zerospace-format.md new file mode 100644 index 0000000..a675cae --- /dev/null +++ b/.junie/plans/rename-projects-to-zerospace-format.md @@ -0,0 +1,117 @@ +--- +sessionId: session-260925-234832-1pc0 +--- + +# Requirements + +### Overview & Goals +Rename all projects in the repository from single-word names (`Build`, `Deploy`, `Model`, `Web`) to qualified names starting with the `ZeroSpace.` prefix (`ZeroSpace.Build`, `ZeroSpace.Deploy`, `ZeroSpace.Model`, `ZeroSpace.Web`). Ensure all solution definitions, project files, folder paths, project references, namespaces, and script paths are updated consistently. + +### Scope +- **In Scope:** + - Renaming project directories: `ZS/Build` → `ZS/ZeroSpace.Build`, `ZS/Deploy` → `ZS/ZeroSpace.Deploy`, `ZS/Model` → `ZS/ZeroSpace.Model`, `ZS/Web` → `ZS/ZeroSpace.Web`. + - Renaming `.csproj` files: `Build.csproj` → `ZeroSpace.Build.csproj`, `Deploy.csproj` → `ZeroSpace.Deploy.csproj`, `Model.csproj` → `ZeroSpace.Model.csproj`, `Web.csproj` → `ZeroSpace.Web.csproj`. + - Updating `ZS/ZeroSpace.sln` project declarations and configuration mappings. + - Updating `` in `ZeroSpace.Web.csproj`. + - Updating C# namespaces and `@using` directives to match the `ZeroSpace.*` convention. + - Updating hardcoded project/folder paths in `ZeroSpace.Build/Program.cs` and `ZeroSpace.Deploy/Program.cs`. +- **Out of Scope:** + - Modifying business logic or UI behavior in the Blazor web application. + - Renaming Markdown documentation files under `zs.docs`. + +# Technical Design + +### Current Implementation +The solution contains 4 projects in `ZS/`: +- `ZS/Model/Model.csproj`: Class library declaring `namespace Model;`. +- `ZS/Build/Build.csproj`: Console generator referencing `Model/Units.g.cs` and scanning for `ZS.sln`. +- `ZS/Deploy/Deploy.csproj`: Deployment tool targeting `../Web/Web.csproj`. +- `ZS/Web/Web.csproj`: Blazor WebAssembly app referencing `..\Model\Model.csproj` and using `Web`, `Web.Layout`, and `Model`. +- `ZS/ZeroSpace.sln`: Solution listing `Web`, `Build`, `Model`, and `Deploy`. + +### Key Decisions +- **Rename Project Directories:** Project folder names will be updated to match the project names (`ZeroSpace.Build`, `ZeroSpace.Deploy`, `ZeroSpace.Model`, `ZeroSpace.Web`) to maintain standard .NET convention where directory name matches the assembly/project name. +- **Update Namespaces:** Align namespaces (`ZeroSpace.Model`, `ZeroSpace.Web`) with project naming for consistency across the solution. +- **Fix Solution Search in Build Tool:** Update `FindSolutionRoot` in `ZeroSpace.Build/Program.cs` to locate `ZeroSpace.sln` (or any `*.sln`). + +### Proposed Changes +- **Project Structure & Naming:** + - `ZS/Build/` → `ZS/ZeroSpace.Build/` + - `ZS/Build/Build.csproj` → `ZS/ZeroSpace.Build/ZeroSpace.Build.csproj` + - `ZS/Deploy/` → `ZS/ZeroSpace.Deploy/` + - `ZS/Deploy/Deploy.csproj` → `ZS/ZeroSpace.Deploy/ZeroSpace.Deploy.csproj` + - `ZS/Model/` → `ZS/ZeroSpace.Model/` + - `ZS/Model/Model.csproj` → `ZS/ZeroSpace.Model/ZeroSpace.Model.csproj` + - `ZS/Web/` → `ZS/ZeroSpace.Web/` + - `ZS/Web/Web.csproj` → `ZS/ZeroSpace.Web/ZeroSpace.Web.csproj` +- **Solution File (`ZS/ZeroSpace.sln`):** + - Update project name strings and project file paths to `ZeroSpace.*`. +- **Project Reference (`ZS/ZeroSpace.Web/ZeroSpace.Web.csproj`):** + - Change `..\Model\Model.csproj` to `..\ZeroSpace.Model\ZeroSpace.Model.csproj`. +- **Source Files & Namespaces:** + - `ZS/ZeroSpace.Model/UnitData.cs`: `namespace ZeroSpace.Model;` + - `ZS/ZeroSpace.Model/UnitEfficiency.cs`: `namespace ZeroSpace.Model;` + - `ZS/ZeroSpace.Model/Units.g.cs`: `namespace ZeroSpace.Model;` + - `ZS/ZeroSpace.Build/Program.cs`: update model path to `ZeroSpace.Model`, generate `namespace ZeroSpace.Model;`, check `ZeroSpace.sln`. + - `ZS/ZeroSpace.Deploy/Program.cs`: update path lookups for `ZeroSpace.Web` and `ZeroSpace.Web.csproj`. + - `ZS/ZeroSpace.Web/Program.cs`: `using ZeroSpace.Web;` + - `ZS/ZeroSpace.Web/_Imports.razor`: `@using ZeroSpace.Web`, `@using ZeroSpace.Web.Layout`, `@using ZeroSpace.Model` + - `ZS/ZeroSpace.Web/Pages/Units.razor`: `@using ZeroSpace.Model` + +### Architecture Diagram +```mermaid +graph TD + Sln[ZS/ZeroSpace.sln] --> Web[ZS/ZeroSpace.Web] + Sln --> Model[ZS/ZeroSpace.Model] + Sln --> Build[ZS/ZeroSpace.Build] + Sln --> Deploy[ZS/ZeroSpace.Deploy] + Web --> Model + Build -. generates Units.g.cs .-> Model + Deploy -. publishes .-> Web +``` + +# Testing + +### Validation Approach +- Verify solution builds cleanly via CLI (`dotnet build ZS/ZeroSpace.sln`). +- Run code generation via `dotnet run --project ZS/ZeroSpace.Build/ZeroSpace.Build.csproj` to confirm `Units.g.cs` is generated with the proper namespace. +- Validate project references and dependencies resolve without warnings or missing assembly errors. + +### Key Scenarios +- `dotnet build ZS/ZeroSpace.sln` succeeds with 0 errors and 0 warnings. +- `ZeroSpace.Web` compiles with reference to `ZeroSpace.Model`. +- `ZeroSpace.Build` locates `zs.docs` and writes generated code into `ZS/ZeroSpace.Model/Units.g.cs`. + +### Edge Cases +- Relative path traversal from build outputs (`AppContext.BaseDirectory` in `ZeroSpace.Deploy`) resolving the renamed `ZeroSpace.Web` project directory. +- `FindSolutionRoot` helper locating `ZeroSpace.sln` accurately. + +# Delivery Steps + +### ✓ Step 1: Rename project directories and project files +Project folders and `.csproj` files follow the `ZeroSpace.*` naming convention. + +- Rename directory `ZS/Model` to `ZS/ZeroSpace.Model` and project file `Model.csproj` to `ZeroSpace.Model.csproj`. +- Rename directory `ZS/Build` to `ZS/ZeroSpace.Build` and project file `Build.csproj` to `ZeroSpace.Build.csproj`. +- Rename directory `ZS/Deploy` to `ZS/ZeroSpace.Deploy` and project file `Deploy.csproj` to `ZeroSpace.Deploy.csproj`. +- Rename directory `ZS/Web` to `ZS/ZeroSpace.Web` and project file `Web.csproj` to `ZeroSpace.Web.csproj`. + +### ✓ Step 2: Update solution file and project references +`ZeroSpace.sln` and project reference paths correctly reference the renamed projects and directories. + +- Update `ZS/ZeroSpace.sln` project entries and relative paths for `ZeroSpace.Web`, `ZeroSpace.Build`, `ZeroSpace.Model`, and `ZeroSpace.Deploy`. +- Update project reference `` in `ZS/ZeroSpace.Web/ZeroSpace.Web.csproj`. + +### ✓ Step 3: Update namespaces and code path references +Source code files, generator scripts, and deployment scripts use the updated `ZeroSpace.*` namespaces and folder paths. + +- Update C# namespaces in `ZS/ZeroSpace.Model/UnitData.cs`, `ZS/ZeroSpace.Model/UnitEfficiency.cs`, and `ZS/ZeroSpace.Model/Units.g.cs` to `ZeroSpace.Model`. +- Update code generator in `ZS/ZeroSpace.Build/Program.cs` to target `ZeroSpace.Model` folder, output `namespace ZeroSpace.Model;`, and verify solution root resolution for `ZeroSpace.sln`. +- Update deploy script paths in `ZS/ZeroSpace.Deploy/Program.cs` to locate `ZeroSpace.Web` and `ZeroSpace.Web.csproj`. +- Update `ZS/ZeroSpace.Web/Program.cs`, `ZS/ZeroSpace.Web/_Imports.razor`, and `ZS/ZeroSpace.Web/Pages/Units.razor` to reference `ZeroSpace.Web`, `ZeroSpace.Web.Layout`, and `ZeroSpace.Model`. + +### ✓ Step 4: Validate solution build and code generation +The entire solution builds cleanly with zero errors/warnings and code generation runs successfully. + +- Run `dotnet build ZS/ZeroSpace.sln` to ensure all projects compile and link without errors. +- Run `ZeroSpace.Build` console application to ensure unit code generation completes and updates `ZeroSpace.Model/Units.g.cs` correctly. \ No newline at end of file diff --git a/ZS/Web/Program.cs b/ZS/Web/Program.cs deleted file mode 100644 index 7e054ad..0000000 --- a/ZS/Web/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.AspNetCore.Components.Web; -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Web; - -var builder = WebAssemblyHostBuilder.CreateDefault(args); -builder.RootComponents.Add("#app"); -builder.RootComponents.Add("head::after"); - -builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); - -builder.Services.AddTelerikBlazor(); - -await builder.Build().RunAsync(); \ No newline at end of file diff --git a/ZS/ZS.sln b/ZS/ZS.sln deleted file mode 100644 index 8227345..0000000 --- a/ZS/ZS.sln +++ /dev/null @@ -1,34 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web", "Web\Web.csproj", "{16296B27-D17B-4BFF-B08F-60F20DF36066}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Build", "Build\Build.csproj", "{C1C04020-AFD6-493B-ABDB-277977D3488E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj", "{E31A06B0-9B2B-4805-B343-5687A5992E9A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Deploy", "Deploy\Deploy.csproj", "{DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {16296B27-D17B-4BFF-B08F-60F20DF36066}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {16296B27-D17B-4BFF-B08F-60F20DF36066}.Debug|Any CPU.Build.0 = Debug|Any CPU - {16296B27-D17B-4BFF-B08F-60F20DF36066}.Release|Any CPU.ActiveCfg = Release|Any CPU - {16296B27-D17B-4BFF-B08F-60F20DF36066}.Release|Any CPU.Build.0 = Release|Any CPU - {C1C04020-AFD6-493B-ABDB-277977D3488E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C1C04020-AFD6-493B-ABDB-277977D3488E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C1C04020-AFD6-493B-ABDB-277977D3488E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C1C04020-AFD6-493B-ABDB-277977D3488E}.Release|Any CPU.Build.0 = Release|Any CPU - {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Release|Any CPU.Build.0 = Release|Any CPU - {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/ZeroSpace/.dockerignore b/ZeroSpace/.dockerignore new file mode 100644 index 0000000..7393196 --- /dev/null +++ b/ZeroSpace/.dockerignore @@ -0,0 +1,9 @@ +**/.git +**/.vs +**/.idea +**/.junie +**/bin +**/obj +**/node_modules +*.tmp +*.log diff --git a/ZS/.junie/memory/errors.md b/ZeroSpace/.junie/memory/errors.md similarity index 100% rename from ZS/.junie/memory/errors.md rename to ZeroSpace/.junie/memory/errors.md diff --git a/ZS/.junie/memory/feedback.md b/ZeroSpace/.junie/memory/feedback.md similarity index 100% rename from ZS/.junie/memory/feedback.md rename to ZeroSpace/.junie/memory/feedback.md diff --git a/ZS/.junie/memory/language.json b/ZeroSpace/.junie/memory/language.json similarity index 100% rename from ZS/.junie/memory/language.json rename to ZeroSpace/.junie/memory/language.json diff --git a/ZS/.junie/memory/memory.version b/ZeroSpace/.junie/memory/memory.version similarity index 100% rename from ZS/.junie/memory/memory.version rename to ZeroSpace/.junie/memory/memory.version diff --git a/ZS/.junie/memory/tasks.md b/ZeroSpace/.junie/memory/tasks.md similarity index 100% rename from ZS/.junie/memory/tasks.md rename to ZeroSpace/.junie/memory/tasks.md diff --git a/ZeroSpace/.junie/plans/add-aspire-k8s-postgres.md b/ZeroSpace/.junie/plans/add-aspire-k8s-postgres.md new file mode 100644 index 0000000..8951c09 --- /dev/null +++ b/ZeroSpace/.junie/plans/add-aspire-k8s-postgres.md @@ -0,0 +1,199 @@ +--- +sessionId: session-260925-235814-7y2w +--- + +# Requirements + +### Overview & Goals +The goal is to modernize the ZeroSpace application by introducing **.NET Aspire** orchestration, adding a dedicated backend **GraphQL API service** powered by **Hot Chocolate** with **Nitro UI (Banana Cake Pop)**, integrating a **PostgreSQL database**, containerizing components with **Docker**, and managing deployments on **Kubernetes** using industry-standard tooling (Aspirate / K8s manifests). + +### Scope +- **In Scope**: + - Creation of `.NET Aspire` AppHost (`ZeroSpace.AppHost`) and shared Service Defaults (`ZeroSpace.ServiceDefaults`). + - Creation of an ASP.NET Core GraphQL API service (`ZeroSpace.ApiService`) using **Hot Chocolate** with Entity Framework Core & PostgreSQL (`Npgsql`) support. + - Interactive **Nitro UI** (Banana Cake Pop) GraphQL IDE integration at the `/graphql` endpoint for rapid schema exploration and testing. + - GraphQL queries, mutations, filtering, sorting, and projections for `UnitData`. + - PostgreSQL container provisioning and database connection orchestration via Aspire. + - Updating `ZeroSpace.Web` (Blazor WASM) to consume GraphQL operations from `ZeroSpace.ApiService`. + - Docker containerization for both `ZeroSpace.ApiService` and `ZeroSpace.Web`. + - Kubernetes manifest management and Aspirate integration for cluster deployment. +- **Out of Scope**: + - Modifying the unit model generation logic in `ZeroSpace.Build`. + - Cloud-specific managed Kubernetes cluster creation scripts (e.g. Terraform/Bicep for AKS/EKS). + +### User Stories +- **As a Developer**, I want to start the web app, GraphQL API, and PostgreSQL database with a single command using .NET Aspire, and use Nitro UI to interactively explore and query schemas so that local development and testing maximize productivity. +- **As an Operator**, I want to generate Kubernetes deployment manifests from the Aspire architecture so that all services and databases deploy consistently to production Kubernetes clusters. +- **As a User**, I want to browse and manage ZeroSpace unit data through the Blazor Web UI backed by efficient, precision GraphQL data fetching and persistent database storage. + +### Functional Requirements +- **FR-1**: The Aspire AppHost must orchestrate the PostgreSQL container, GraphQL API service, and Web app with configured dependencies and environment variables. +- **FR-2**: The `ZeroSpace.ApiService` must expose a Hot Chocolate GraphQL server with schema filtering, sorting, and projection capabilities over PostgreSQL `UnitData`. +- **FR-3**: The Nitro UI GraphQL IDE must be hosted and accessible at `/graphql` to enable in-browser schema exploration, query execution, and debugging. +- **FR-4**: Database schema migrations and initial seed data from `ZeroSpace.Model.Units.All` must be executed automatically upon startup. +- **FR-5**: `ZeroSpace.Web` must execute GraphQL queries/mutations against `ZeroSpace.ApiService` using configured HTTP endpoints. +- **FR-6**: Container definitions (Dockerfiles) must exist for all custom applications. +- **FR-7**: Kubernetes manifests must specify Deployments, Services, PersistentVolumeClaims, Secrets, and Ingress routing for Web and GraphQL endpoints. + +### Non-Functional Requirements +- **Observability**: OpenTelemetry tracing, metrics, and structured logging enabled across services via `ServiceDefaults` (including Hot Chocolate diagnostic tracing). +- **Resilience & Health**: Health check endpoints (`/health`, `/alive`) configured for Kubernetes readiness and liveness probes. +- **Security**: Database credentials managed through environment variables, Secrets, and Aspire connection strings without hardcoded passwords. +- **Query Efficiency**: GraphQL projections ensure only requested fields are fetched from PostgreSQL, minimizing network transfer and database payload overhead. + +# Technical Design + +### Current Implementation +- `ZeroSpace.Web` is a standalone Blazor WebAssembly application (`Microsoft.NET.Sdk.BlazorWebAssembly`) targeting .NET 10 with Telerik UI for Blazor. +- `ZeroSpace.Model` contains records like `UnitData.cs`, `UnitEfficiency.cs`, and generated static unit arrays in `Units.g.cs`. +- `ZeroSpace.Deploy` publishes static files to Azure Static Web Apps. +- `ZeroSpace.AppHost`, `ZeroSpace.ServiceDefaults`, and `ZeroSpace.ApiService` are integrated into the solution. + +### Key Decisions +1. **Hot Chocolate GraphQL Server with Nitro UI (`ZeroSpace.ApiService`)**: + - *Decision*: Introduce Hot Chocolate GraphQL server (`HotChocolate.AspNetCore`, `HotChocolate.Data.EntityFramework`) and enable the embedded Nitro UI (Banana Cake Pop) GraphQL tool suite. + - *Rationale*: GraphQL enables client-driven data fetching, reducing over-fetching and network serialization overhead. Nitro UI provides a rich developer and operator environment for real-time schema introspection, query building, and mutation execution directly in the browser. +2. **.NET Aspire Orchestration & Aspirate for Kubernetes**: + - *Decision*: Use .NET Aspire AppHost for local orchestration and developer dashboard, and use Aspirate (Aspir8) alongside standard Kubernetes manifests. + - *Rationale*: Aspire provides official .NET cloud-native developer tooling, OpenTelemetry defaults, and service discovery, while Aspirate transforms the Aspire manifest into production-ready Kubernetes Helm/Kustomize/YAML artifacts. +3. **Database Architecture & Seeding**: + - *Decision*: Entity Framework Core with `Npgsql.EntityFrameworkCore.PostgreSQL` and PostgreSQL 17 container. + - *Rationale*: Native EF Core support with Aspire component `Aspire.Npgsql.EntityFrameworkCore.PostgreSQL` enables automatic connection string binding, resilient retries, and database health checks. + +### Architecture Diagram +```mermaid +graph TD + subgraph Local / Dev [Local .NET Aspire AppHost] + AppHost[ZeroSpace.AppHost] + Dash[Aspire Dashboard] + Nitro[Nitro UI / Banana Cake Pop] + AppHost --> Dash + AppHost --> Nitro + end + + subgraph Kubernetes Cluster [Kubernetes Cluster] + Ingress[Kubernetes Ingress / Gateway] + + subgraph Web Pod [Web Deployment] + Web[ZeroSpace.Web / Nginx] + end + + subgraph API Pod [API Deployment] + API[ZeroSpace.ApiService / Hot Chocolate] + end + + subgraph DB Pod [PostgreSQL StatefulSet / Pod] + DB[(PostgreSQL 17)] + PVC[PersistentVolumeClaim] + DB --- PVC + end + end + + Ingress --> Web + Ingress --> API + Web -->|GraphQL Queries / Mutations| API + Nitro -->|Interactive Queries / Schema Docs| API + API -->|EF Core / Npgsql| DB +``` + +### Components +- **`ZeroSpace.AppHost`**: Aspire orchestrator project that links PostgreSQL container, `ZeroSpace.ApiService`, and `ZeroSpace.Web`. +- **`ZeroSpace.ServiceDefaults`**: Shared project configuring OpenTelemetry, health checks, discovery, and resilience policies. +- **`ZeroSpace.ApiService`**: ASP.NET Core GraphQL API powered by Hot Chocolate, hosting Nitro UI at `/graphql`, and handling PostgreSQL persistence with EF Core. +- **`ZeroSpace.Web`**: Client-side Blazor WASM UI configured with `IUnitsApiClient` communicating with `ZeroSpace.ApiService` via GraphQL. +- **`k8s/`**: Kubernetes manifests (Deployments, Services, PVC, Ingress, Secrets) and Aspirate generation configuration. + +### File Structure +``` +ZeroSpace/ +├── ZeroSpace.AppHost/ +│ ├── Program.cs +│ ├── appsettings.json +│ └── ZeroSpace.AppHost.csproj +├── ZeroSpace.ServiceDefaults/ +│ ├── Extensions.cs +│ └── ZeroSpace.ServiceDefaults.csproj +├── ZeroSpace.ApiService/ +│ ├── Data/ +│ │ ├── ZeroSpaceDbContext.cs +│ │ └── DbInitializer.cs +│ ├── GraphQL/ +│ │ ├── Query.cs +│ │ ├── Mutation.cs +│ │ └── Types/ +│ ├── Dockerfile +│ ├── Program.cs +│ └── ZeroSpace.ApiService.csproj +├── ZeroSpace.Web/ +│ ├── Services/ +│ │ ├── IUnitsApiClient.cs +│ │ └── UnitsApiClient.cs +│ ├── Dockerfile +│ ├── nginx.conf +│ └── ... +├── k8s/ +│ ├── postgres-pvc.yaml +│ ├── postgres-deployment.yaml +│ ├── apiservice-deployment.yaml +│ ├── web-deployment.yaml +│ └── ingress.yaml +├── aspirate.json +└── ZeroSpace.sln +``` + +### Data Models / Contracts +```graphql + +# GraphQL Schema Contract + +type Query { + units( + where: UnitDataFilterInput + order: [UnitDataSortInput!] + ): [UnitData!]! + + unitById(id: String!): UnitData +} + +type Mutation { + createUnit(input: CreateUnitInput!): UnitData! + updateUnit(id: String!, input: UpdateUnitInput!): UnitData! + deleteUnit(id: String!): Boolean! +} +``` + +### Risks & Mitigations +- **Risk**: GraphQL N+1 or deep query performance impacts. + - *Mitigation*: Leverage Hot Chocolate's `[UseProjection]`, `[UseFiltering]`, and `[UseSorting]` which translate GraphQL AST directly into optimized PostgreSQL SQL via EF Core `IQueryable`. +- **Risk**: Nitro UI access across different deployment environments. + - *Mitigation*: Configure Nitro UI / Banana Cake Pop middleware settings so developer tooling is available locally and behind authorized Ingress routes in staging/production as needed. +- **Risk**: Blazor WASM CORS issues when executing GraphQL POST requests. + - *Mitigation*: Configure permissive CORS policies in `ZeroSpace.ApiService` for GraphQL endpoints and route requests through Kubernetes Ingress. + +# Testing + +### Validation Approach +- Verify local orchestration via `dotnet run --project ZeroSpace.AppHost`. +- Access Nitro UI at `http://localhost:5142/graphql` (or through Aspire Dashboard endpoint) to test interactive queries, mutations, and schema documentation. +- Test GraphQL queries and mutations executed by `ZeroSpace.Web` against `ZeroSpace.ApiService`. +- Build and run Docker containers for `ZeroSpace.ApiService` and `ZeroSpace.Web`. +- Validate Kubernetes manifest syntax and Aspirate manifest compilation (`aspirate generate`). + +### Key Scenarios +1. **Aspire Local Launch**: Starting `ZeroSpace.AppHost` boots PostgreSQL container, runs EF Core migrations, starts `ZeroSpace.ApiService` with Hot Chocolate GraphQL & Nitro UI, and launches `ZeroSpace.Web`. +2. **Nitro UI Interactive Execution**: Open `/graphql` in browser; Banana Cake Pop / Nitro UI loads; execute `{ units { id name hexite flux tier faction } }` and receive structured JSON response. +3. **Blazor UI Data Flow**: Navigating to `/units` in the Blazor UI sends a GraphQL query payload to `ZeroSpace.ApiService`, which queries PostgreSQL and returns unit records. +4. **Container Build**: `docker build` succeeds for both `ZeroSpace.ApiService` and `ZeroSpace.Web` Dockerfiles. +5. **Kubernetes Deployment**: Applying the generated Kubernetes manifests provisions the PVC, deploys PostgreSQL, starts API/Web pods, and exposes them via Service and Ingress. + +### Edge Cases +- **PostgreSQL Connection Failures**: GraphQL queries gracefully return structured GraphQL errors and health checks report unhealthy status. +- **Empty Database**: Seeding logic populates initial units from `ZeroSpace.Model` if the database table is empty before serving GraphQL requests. +- **Complex GraphQL Filtering**: Verify filtering by tier and faction compiles to efficient SQL `WHERE` clauses in PostgreSQL without in-memory buffering. + +# Execution Plan + +### ✓ Step 1: Configure Hot Chocolate GraphQL and Nitro UI in ZeroSpace.ApiService +### ✓ Step 2: Update ZeroSpace.Web to consume GraphQL API operations +### ✓ Step 3: Update AppHost, Ingress, and Kubernetes configuration for GraphQL endpoint +### ✓ Step 4: Build and Verify Solution \ No newline at end of file diff --git a/ZeroSpace/ZeroSpace.ApiService/Data/DbInitializer.cs b/ZeroSpace/ZeroSpace.ApiService/Data/DbInitializer.cs new file mode 100644 index 0000000..c3f4d74 --- /dev/null +++ b/ZeroSpace/ZeroSpace.ApiService/Data/DbInitializer.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using ZeroSpace.Model; + +namespace ZeroSpace.ApiService.Data; + +public static class DbInitializer +{ + public static async Task InitializeDatabaseAsync(IServiceProvider serviceProvider, ILogger logger, CancellationToken cancellationToken = default) + { + using var scope = serviceProvider.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + + logger.LogInformation("Ensuring database schema exists..."); + await db.Database.EnsureCreatedAsync(cancellationToken); + + if (!await db.Units.AnyAsync(cancellationToken)) + { + logger.LogInformation("Seeding initial units into PostgreSQL..."); + var initialUnits = Units.All; + await db.Units.AddRangeAsync(initialUnits, cancellationToken); + await db.SaveChangesAsync(cancellationToken); + logger.LogInformation("Seeded {Count} units into PostgreSQL database.", initialUnits.Count); + } + else + { + logger.LogInformation("Database already contains unit data. Skipping seed."); + } + } +} diff --git a/ZeroSpace/ZeroSpace.ApiService/Data/ZeroSpaceDbContext.cs b/ZeroSpace/ZeroSpace.ApiService/Data/ZeroSpaceDbContext.cs new file mode 100644 index 0000000..28ac293 --- /dev/null +++ b/ZeroSpace/ZeroSpace.ApiService/Data/ZeroSpaceDbContext.cs @@ -0,0 +1,66 @@ +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using ZeroSpace.Model; + +namespace ZeroSpace.ApiService.Data; + +public class ZeroSpaceDbContext : DbContext +{ + public ZeroSpaceDbContext(DbContextOptions options) : base(options) + { + } + + public DbSet Units => Set(); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(entity => + { + entity.ToTable("units"); + entity.HasKey(e => e.Id); + + entity.Property(e => e.Id).HasColumnName("id").HasMaxLength(100); + entity.Property(e => e.Name).HasColumnName("name").IsRequired().HasMaxLength(200); + entity.Property(e => e.Hexite).HasColumnName("hexite"); + entity.Property(e => e.Flux).HasColumnName("flux"); + entity.Property(e => e.Supply).HasColumnName("supply"); + entity.Property(e => e.ProductionTime).HasColumnName("production_time"); + entity.Property(e => e.Health).HasColumnName("health"); + entity.Property(e => e.Energy).HasColumnName("energy"); + entity.Property(e => e.Shields).HasColumnName("shields"); + entity.Property(e => e.ArmorRating).HasColumnName("armor_rating"); + entity.Property(e => e.MovementSpeed).HasColumnName("movement_speed"); + entity.Property(e => e.DamagePerSecond).HasColumnName("damage_per_second"); + entity.Property(e => e.AttackRange).HasColumnName("attack_range"); + entity.Property(e => e.Tier).HasColumnName("tier"); + entity.Property(e => e.Faction).HasColumnName("faction").IsRequired().HasMaxLength(100); + entity.Property(e => e.Hotkey).HasColumnName("hotkey").HasMaxLength(20); + entity.Property(e => e.BuildAtSameTime).HasColumnName("build_at_same_time"); + entity.Property(e => e.Limit).HasColumnName("limit"); + + var valueComparer = new ValueComparer>( + (c1, c2) => (c1 == null && c2 == null) || (c1 != null && c2 != null && c1.SequenceEqual(c2)), + c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), + c => c.ToList() + ); + + entity.Property(e => e.Attributes) + .HasColumnName("attributes") + .HasConversion( + v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null), + v => (IReadOnlyList)(JsonSerializer.Deserialize>(v, (JsonSerializerOptions?)null) ?? new List()) + ) + .Metadata.SetValueComparer(valueComparer); + + entity.Ignore(e => e.DpsPerHexite); + entity.Ignore(e => e.DpsPerFlux); + entity.Ignore(e => e.DpsPerTotalCost); + entity.Ignore(e => e.HealthPerHexite); + entity.Ignore(e => e.HealthPerFlux); + entity.Ignore(e => e.HealthPerTotalCost); + }); + } +} diff --git a/ZeroSpace/ZeroSpace.ApiService/Dockerfile b/ZeroSpace/ZeroSpace.ApiService/Dockerfile new file mode 100644 index 0000000..42f8a8b --- /dev/null +++ b/ZeroSpace/ZeroSpace.ApiService/Dockerfile @@ -0,0 +1,21 @@ +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +WORKDIR /src + +# Copy project files for caching restore layer +COPY ["ZeroSpace.ApiService/ZeroSpace.ApiService.csproj", "ZeroSpace.ApiService/"] +COPY ["ZeroSpace.Model/ZeroSpace.Model.csproj", "ZeroSpace.Model/"] +COPY ["ZeroSpace.ServiceDefaults/ZeroSpace.ServiceDefaults.csproj", "ZeroSpace.ServiceDefaults/"] + +RUN dotnet restore "ZeroSpace.ApiService/ZeroSpace.ApiService.csproj" + +# Copy source code and build +COPY . . +WORKDIR "/src/ZeroSpace.ApiService" +RUN dotnet publish "ZeroSpace.ApiService.csproj" -c Release -o /app/publish /p:UseAppHost=false + +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final +WORKDIR /app +EXPOSE 8080 +EXPOSE 8443 +COPY --from=build /app/publish . +ENTRYPOINT ["dotnet", "ZeroSpace.ApiService.dll"] diff --git a/ZeroSpace/ZeroSpace.ApiService/Endpoints/UnitsEndpoints.cs b/ZeroSpace/ZeroSpace.ApiService/Endpoints/UnitsEndpoints.cs new file mode 100644 index 0000000..f676e4e --- /dev/null +++ b/ZeroSpace/ZeroSpace.ApiService/Endpoints/UnitsEndpoints.cs @@ -0,0 +1,93 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.EntityFrameworkCore; +using ZeroSpace.ApiService.Data; +using ZeroSpace.Model; + +namespace ZeroSpace.ApiService.Endpoints; + +public static class UnitsEndpoints +{ + public static IEndpointRouteBuilder MapUnitsEndpoints(this IEndpointRouteBuilder routes) + { + var group = routes.MapGroup("/api/units") + .WithTags("Units"); + + group.MapGet("/", async (ZeroSpaceDbContext db, string? faction, int? tier) => + { + IQueryable query = db.Units.AsNoTracking(); + + if (!string.IsNullOrWhiteSpace(faction)) + { + query = query.Where(u => u.Faction == faction); + } + + if (tier.HasValue) + { + query = query.Where(u => u.Tier == tier.Value); + } + + var units = await query.ToListAsync(); + return Results.Ok(units); + }) + .WithName("GetAllUnits") + .WithSummary("Get all units optionally filtered by faction and tier."); + + group.MapGet("/{id}", async (string id, ZeroSpaceDbContext db) => + { + var unit = await db.Units.AsNoTracking().FirstOrDefaultAsync(u => u.Id == id); + return unit is not null ? Results.Ok(unit) : Results.NotFound(); + }) + .WithName("GetUnitById") + .WithSummary("Get a unit by id."); + + group.MapPost("/", async (UnitData unit, ZeroSpaceDbContext db) => + { + var exists = await db.Units.AnyAsync(u => u.Id == unit.Id); + if (exists) + { + return Results.Conflict($"Unit with id '{unit.Id}' already exists."); + } + + db.Units.Add(unit); + await db.SaveChangesAsync(); + return Results.Created($"/api/units/{unit.Id}", unit); + }) + .WithName("CreateUnit") + .WithSummary("Create a new unit."); + + group.MapPut("/{id}", async (string id, UnitData updatedUnit, ZeroSpaceDbContext db) => + { + var unit = await db.Units.FindAsync(id); + if (unit is null) + { + return Results.NotFound(); + } + + db.Units.Remove(unit); + db.Units.Add(updatedUnit with { Id = id }); + await db.SaveChangesAsync(); + return Results.Ok(updatedUnit with { Id = id }); + }) + .WithName("UpdateUnit") + .WithSummary("Update an existing unit."); + + group.MapDelete("/{id}", async (string id, ZeroSpaceDbContext db) => + { + var unit = await db.Units.FindAsync(id); + if (unit is null) + { + return Results.NotFound(); + } + + db.Units.Remove(unit); + await db.SaveChangesAsync(); + return Results.NoContent(); + }) + .WithName("DeleteUnit") + .WithSummary("Delete a unit by id."); + + return routes; + } +} diff --git a/ZeroSpace/ZeroSpace.ApiService/GraphQL/Mutation.cs b/ZeroSpace/ZeroSpace.ApiService/GraphQL/Mutation.cs new file mode 100644 index 0000000..83a33e5 --- /dev/null +++ b/ZeroSpace/ZeroSpace.ApiService/GraphQL/Mutation.cs @@ -0,0 +1,136 @@ +using HotChocolate; +using Microsoft.EntityFrameworkCore; +using ZeroSpace.ApiService.Data; +using ZeroSpace.Model; + +namespace ZeroSpace.ApiService.GraphQL; + +public record CreateUnitInput( + string Id, + string Name, + int Hexite, + int Flux, + int Supply, + int ProductionTime, + int Health, + int Energy, + int Shields, + int ArmorRating, + int MovementSpeed, + int DamagePerSecond, + int AttackRange, + IReadOnlyList? Attributes, + int Tier, + string Faction, + string? Hotkey, + int BuildAtSameTime, + int? Limit +); + +public record UpdateUnitInput( + string Name, + int Hexite, + int Flux, + int Supply, + int ProductionTime, + int Health, + int Energy, + int Shields, + int ArmorRating, + int MovementSpeed, + int DamagePerSecond, + int AttackRange, + IReadOnlyList? Attributes, + int Tier, + string Faction, + string? Hotkey, + int BuildAtSameTime, + int? Limit +); + +public class Mutation +{ + public async Task CreateUnitAsync(CreateUnitInput input, ZeroSpaceDbContext db, CancellationToken cancellationToken) + { + var exists = await db.Units.AnyAsync(u => u.Id == input.Id, cancellationToken); + if (exists) + { + throw new GraphQLException(ErrorBuilder.New().SetMessage($"Unit with id '{input.Id}' already exists.").SetCode("UNIT_ALREADY_EXISTS").Build()); + } + + var unit = new UnitData( + input.Id, + input.Name, + input.Hexite, + input.Flux, + input.Supply, + input.ProductionTime, + input.Health, + input.Energy, + input.Shields, + input.ArmorRating, + input.MovementSpeed, + input.DamagePerSecond, + input.AttackRange, + input.Attributes ?? new List(), + input.Tier, + input.Faction, + input.Hotkey, + input.BuildAtSameTime, + input.Limit + ); + + db.Units.Add(unit); + await db.SaveChangesAsync(cancellationToken); + return unit; + } + + public async Task UpdateUnitAsync(string id, UpdateUnitInput input, ZeroSpaceDbContext db, CancellationToken cancellationToken) + { + var existing = await db.Units.FindAsync(new object[] { id }, cancellationToken); + if (existing is null) + { + throw new GraphQLException(ErrorBuilder.New().SetMessage($"Unit with id '{id}' not found.").SetCode("UNIT_NOT_FOUND").Build()); + } + + var updated = new UnitData( + id, + input.Name, + input.Hexite, + input.Flux, + input.Supply, + input.ProductionTime, + input.Health, + input.Energy, + input.Shields, + input.ArmorRating, + input.MovementSpeed, + input.DamagePerSecond, + input.AttackRange, + input.Attributes ?? new List(), + input.Tier, + input.Faction, + input.Hotkey, + input.BuildAtSameTime, + input.Limit + ); + + db.Units.Remove(existing); + db.Units.Add(updated); + await db.SaveChangesAsync(cancellationToken); + return updated; + } + + public async Task DeleteUnitAsync(string id, ZeroSpaceDbContext db, CancellationToken cancellationToken) + { + var unit = await db.Units.FindAsync(new object[] { id }, cancellationToken); + if (unit is null) + { + return false; + } + + db.Units.Remove(unit); + await db.SaveChangesAsync(cancellationToken); + return true; + } +} diff --git a/ZeroSpace/ZeroSpace.ApiService/GraphQL/Query.cs b/ZeroSpace/ZeroSpace.ApiService/GraphQL/Query.cs new file mode 100644 index 0000000..7a5be2b --- /dev/null +++ b/ZeroSpace/ZeroSpace.ApiService/GraphQL/Query.cs @@ -0,0 +1,19 @@ +using HotChocolate; +using HotChocolate.Data; +using Microsoft.EntityFrameworkCore; +using ZeroSpace.ApiService.Data; +using ZeroSpace.Model; + +namespace ZeroSpace.ApiService.GraphQL; + +public class Query +{ + [UseProjection] + [UseFiltering] + [UseSorting] + public IQueryable GetUnits(ZeroSpaceDbContext db) => + db.Units.AsNoTracking(); + + public async Task GetUnitByIdAsync(string id, ZeroSpaceDbContext db, CancellationToken cancellationToken) => + await db.Units.AsNoTracking().FirstOrDefaultAsync(u => u.Id == id, cancellationToken); +} diff --git a/ZeroSpace/ZeroSpace.ApiService/Program.cs b/ZeroSpace/ZeroSpace.ApiService/Program.cs new file mode 100644 index 0000000..de0c3a5 --- /dev/null +++ b/ZeroSpace/ZeroSpace.ApiService/Program.cs @@ -0,0 +1,64 @@ +using ZeroSpace.ApiService.Data; +using ZeroSpace.ApiService.Endpoints; +using ZeroSpace.ApiService.GraphQL; + +var builder = WebApplication.CreateBuilder(args); + +// Add service defaults & Aspire components +builder.AddServiceDefaults(); + +// Add EF Core with PostgreSQL connection from Aspire ("zerospacedb") +builder.AddNpgsqlDbContext("zerospacedb"); + +// Add CORS for Web client +builder.Services.AddCors(options => +{ + options.AddDefaultPolicy(policy => + { + policy.AllowAnyOrigin() + .AllowAnyHeader() + .AllowAnyMethod(); + }); +}); + +// Configure Hot Chocolate GraphQL Server with Nitro UI (Banana Cake Pop) +builder.Services + .AddGraphQLServer() + .AddQueryType() + .AddMutationType() + .AddProjections() + .AddFiltering() + .AddSorting(); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +app.UseCors(); + +app.MapDefaultEndpoints(); + +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); +} + +// Map GraphQL endpoint with Nitro UI (Banana Cake Pop) at /graphql +app.MapGraphQL(); + +// Optional REST endpoints for auxiliary compatibility +app.MapUnitsEndpoints(); + +// Initialize database schema and initial data +try +{ + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + await DbInitializer.InitializeDatabaseAsync(app.Services, app.Logger, cts.Token); +} +catch (Exception ex) +{ + app.Logger.LogError(ex, "An error occurred while initializing the database."); +} + +await app.RunAsync(); diff --git a/ZeroSpace/ZeroSpace.ApiService/Properties/launchSettings.json b/ZeroSpace/ZeroSpace.ApiService/Properties/launchSettings.json new file mode 100644 index 0000000..a31aa5e --- /dev/null +++ b/ZeroSpace/ZeroSpace.ApiService/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7142;http://localhost:5142", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5142", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/ZeroSpace/ZeroSpace.ApiService/ZeroSpace.ApiService.csproj b/ZeroSpace/ZeroSpace.ApiService/ZeroSpace.ApiService.csproj new file mode 100644 index 0000000..c990c4c --- /dev/null +++ b/ZeroSpace/ZeroSpace.ApiService/ZeroSpace.ApiService.csproj @@ -0,0 +1,21 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/ZeroSpace/ZeroSpace.ApiService/appsettings.Development.json b/ZeroSpace/ZeroSpace.ApiService/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/ZeroSpace/ZeroSpace.ApiService/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/ZeroSpace/ZeroSpace.ApiService/appsettings.json b/ZeroSpace/ZeroSpace.ApiService/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/ZeroSpace/ZeroSpace.ApiService/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/ZeroSpace/ZeroSpace.AppHost/Program.cs b/ZeroSpace/ZeroSpace.AppHost/Program.cs new file mode 100644 index 0000000..114fecc --- /dev/null +++ b/ZeroSpace/ZeroSpace.AppHost/Program.cs @@ -0,0 +1,15 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var postgres = builder.AddPostgres("postgres") + .WithDataVolume() + .AddDatabase("zerospacedb"); + +var apiService = builder.AddProject("apiservice") + .WithReference(postgres) + .WaitFor(postgres); + +var web = builder.AddProject("web") + .WithReference(apiService) + .WaitFor(apiService); + +builder.Build().Run(); diff --git a/ZeroSpace/ZeroSpace.AppHost/Properties/launchSettings.json b/ZeroSpace/ZeroSpace.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000..1bf4cef --- /dev/null +++ b/ZeroSpace/ZeroSpace.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17228;http://localhost:15228", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21145", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15228", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19145", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" + } + } + } +} diff --git a/ZeroSpace/ZeroSpace.AppHost/ZeroSpace.AppHost.csproj b/ZeroSpace/ZeroSpace.AppHost/ZeroSpace.AppHost.csproj new file mode 100644 index 0000000..51c71ef --- /dev/null +++ b/ZeroSpace/ZeroSpace.AppHost/ZeroSpace.AppHost.csproj @@ -0,0 +1,24 @@ + + + + Exe + net10.0 + enable + enable + true + zerospace-apphost-secrets + $(NoWarn);ASPIRE010 + + + + + + + + + + + + + + diff --git a/ZeroSpace/ZeroSpace.AppHost/appsettings.Development.json b/ZeroSpace/ZeroSpace.AppHost/appsettings.Development.json new file mode 100644 index 0000000..f08c5c7 --- /dev/null +++ b/ZeroSpace/ZeroSpace.AppHost/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "DetailedErrors": true, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "ResourceLifecycle": "Information" + } + } +} diff --git a/ZeroSpace/ZeroSpace.AppHost/appsettings.json b/ZeroSpace/ZeroSpace.AppHost/appsettings.json new file mode 100644 index 0000000..0b3082e --- /dev/null +++ b/ZeroSpace/ZeroSpace.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "ResourceLifecycle": "Information" + } + } +} diff --git a/ZS/Build/Program.cs b/ZeroSpace/ZeroSpace.Build/Program.cs similarity index 92% rename from ZS/Build/Program.cs rename to ZeroSpace/ZeroSpace.Build/Program.cs index a802936..b1568de 100644 --- a/ZS/Build/Program.cs +++ b/ZeroSpace/ZeroSpace.Build/Program.cs @@ -5,7 +5,7 @@ var solutionRoot = FindSolutionRoot(); var docsRoot = args.Length > 0 ? Path.GetFullPath(args[0]) : Path.GetFullPath(Path.Combine(solutionRoot, "..", "zs.docs")); -var modelRoot = Path.Combine(solutionRoot, "Model"); +var modelRoot = Path.Combine(solutionRoot, "ZeroSpace.Model"); var outputPath = Path.Combine(modelRoot, "Units.g.cs"); if (!Directory.Exists(docsRoot)) @@ -21,7 +21,7 @@ if (!Directory.Exists(modelRoot)) var units = new List(); var skipped = new List(); -foreach (var path in Directory.EnumerateFiles(docsRoot, "*.md").Order(StringComparer.OrdinalIgnoreCase)) +foreach (var path in Directory.EnumerateFiles(docsRoot, "*.md", SearchOption.AllDirectories).Order(StringComparer.OrdinalIgnoreCase)) { var frontMatter = ReadFrontMatter(path); if (frontMatter is null) @@ -71,19 +71,29 @@ if (skipped.Count > 0) static string FindSolutionRoot() { - var directory = new DirectoryInfo(Environment.CurrentDirectory); + var candidates = new[] { Environment.CurrentDirectory, AppContext.BaseDirectory }; - while (directory is not null) + foreach (var candidate in candidates) { - if (directory.EnumerateFiles("ZS.sln").Any()) - { - return directory.FullName; - } + var directory = new DirectoryInfo(candidate); - directory = directory.Parent; + while (directory is not null) + { + if (File.Exists(Path.Combine(directory.FullName, "ZeroSpace.sln"))) + { + return directory.FullName; + } + + if (File.Exists(Path.Combine(directory.FullName, "ZeroSpace", "ZeroSpace.sln"))) + { + return Path.Combine(directory.FullName, "ZeroSpace"); + } + + directory = directory.Parent; + } } - throw new DirectoryNotFoundException("Could not find ZS.sln in the current directory or its parents."); + throw new DirectoryNotFoundException("Could not find ZeroSpace.sln in the current directory or its parents."); } static Dictionary? ReadFrontMatter(string path) @@ -312,7 +322,7 @@ static string GenerateUnits(IReadOnlyList units) { var builder = new StringBuilder(); builder.AppendLine("// "); - builder.AppendLine("namespace Model;"); + builder.AppendLine("namespace ZeroSpace.Model;"); builder.AppendLine(); builder.AppendLine("public static class Units"); builder.AppendLine("{"); diff --git a/ZS/Build/Build.csproj b/ZeroSpace/ZeroSpace.Build/ZeroSpace.Build.csproj similarity index 100% rename from ZS/Build/Build.csproj rename to ZeroSpace/ZeroSpace.Build/ZeroSpace.Build.csproj diff --git a/ZS/Deploy/Program.cs b/ZeroSpace/ZeroSpace.Deploy/Program.cs similarity index 89% rename from ZS/Deploy/Program.cs rename to ZeroSpace/ZeroSpace.Deploy/Program.cs index d684f2a..1868a35 100644 --- a/ZS/Deploy/Program.cs +++ b/ZeroSpace/ZeroSpace.Deploy/Program.cs @@ -1,8 +1,8 @@ using System.Diagnostics; var deployDir = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..")); -var webDir = Path.GetFullPath(Path.Combine(deployDir, "..", "Web")); -var webProject = Path.Combine(webDir, "Web.csproj"); +var webDir = Path.GetFullPath(Path.Combine(deployDir, "..", "ZeroSpace.Web")); +var webProject = Path.Combine(webDir, "ZeroSpace.Web.csproj"); var publishDir = Path.Combine(Path.GetTempPath(), "zs-deploy", Guid.NewGuid().ToString()); var deploymentToken = Environment.GetEnvironmentVariable("ZS_DeployToken") ?? throw new InvalidOperationException("ZS_DeployToken environment variable not set."); var deployEnv = Environment.GetEnvironmentVariable("ZS_DeployEnv") ?? "preview"; diff --git a/ZS/Deploy/Deploy.csproj b/ZeroSpace/ZeroSpace.Deploy/ZeroSpace.Deploy.csproj similarity index 100% rename from ZS/Deploy/Deploy.csproj rename to ZeroSpace/ZeroSpace.Deploy/ZeroSpace.Deploy.csproj diff --git a/ZS/Model/UnitData.cs b/ZeroSpace/ZeroSpace.Model/UnitData.cs similarity index 97% rename from ZS/Model/UnitData.cs rename to ZeroSpace/ZeroSpace.Model/UnitData.cs index fac8948..9da3038 100644 --- a/ZS/Model/UnitData.cs +++ b/ZeroSpace/ZeroSpace.Model/UnitData.cs @@ -1,4 +1,4 @@ -namespace Model; +namespace ZeroSpace.Model; public sealed record UnitData( string Id, diff --git a/ZS/Model/UnitEfficiency.cs b/ZeroSpace/ZeroSpace.Model/UnitEfficiency.cs similarity index 97% rename from ZS/Model/UnitEfficiency.cs rename to ZeroSpace/ZeroSpace.Model/UnitEfficiency.cs index acec1b1..d860446 100644 --- a/ZS/Model/UnitEfficiency.cs +++ b/ZeroSpace/ZeroSpace.Model/UnitEfficiency.cs @@ -1,4 +1,4 @@ -namespace Model; +namespace ZeroSpace.Model; public static class UnitEfficiency { diff --git a/ZS/Model/Units.g.cs b/ZeroSpace/ZeroSpace.Model/Units.g.cs similarity index 81% rename from ZS/Model/Units.g.cs rename to ZeroSpace/ZeroSpace.Model/Units.g.cs index 1620d45..a3538cc 100644 --- a/ZS/Model/Units.g.cs +++ b/ZeroSpace/ZeroSpace.Model/Units.g.cs @@ -1,5 +1,5 @@ // -namespace Model; +namespace ZeroSpace.Model; public static class Units { @@ -150,48 +150,6 @@ public static class Units BuildAtSameTime: 1, Limit: null); - public static readonly UnitData Lasher = new( - Id: "Lasher", - Name: "Lasher", - Hexite: 50, - Flux: 25, - Supply: 2, - ProductionTime: 26, - Health: 150, - Energy: 0, - Shields: 0, - ArmorRating: 0, - MovementSpeed: 550, - DamagePerSecond: 14, - AttackRange: 8, - Attributes: ["Light Armor", "Biological", "Bonus Damage vs Medium Armor"], - Tier: 1, - Faction: "Grell", - Hotkey: "R", - BuildAtSameTime: 1, - Limit: null); - - public static readonly UnitData Skrell = new( - Id: "Skrell", - Name: "Skrell", - Hexite: 12, - Flux: 19, - Supply: 1, - ProductionTime: 35, - Health: 50, - Energy: 0, - Shields: 0, - ArmorRating: 0, - MovementSpeed: 900, - DamagePerSecond: 5, - AttackRange: 2, - Attributes: ["Air unit", "Light Armor", "Biological", "Bonus Damage vs Air"], - Tier: 1, - Faction: "Grell", - Hotkey: "T", - BuildAtSameTime: 4, - Limit: null); - public static readonly UnitData Stinger = new( Id: "Stinger", Name: "Stinger", @@ -255,27 +213,6 @@ public static class Units BuildAtSameTime: 1, Limit: null); - public static readonly UnitData Thresher = new( - Id: "Thresher", - Name: "Thresher", - Hexite: 100, - Flux: 175, - Supply: 8, - ProductionTime: 65, - Health: 300, - Energy: 0, - Shields: 0, - ArmorRating: 0, - MovementSpeed: 450, - DamagePerSecond: 36, - AttackRange: 27, - Attributes: ["Biological", "Heavy Armor", "Bonus Damage vs Buildings"], - Tier: 2, - Faction: "Grell", - Hotkey: "S", - BuildAtSameTime: 1, - Limit: null); - public static readonly UnitData Behemoth = new( Id: "Behemoth", Name: "Behemoth", @@ -348,12 +285,9 @@ public static class Units CorsairFlamer, Gatherer, BroodGuard, - Lasher, - Skrell, Stinger, Harbinger, Mandragora, - Thresher, Behemoth, Reaver, Weaver, diff --git a/ZS/Model/Model.csproj b/ZeroSpace/ZeroSpace.Model/ZeroSpace.Model.csproj similarity index 100% rename from ZS/Model/Model.csproj rename to ZeroSpace/ZeroSpace.Model/ZeroSpace.Model.csproj diff --git a/ZeroSpace/ZeroSpace.ServiceDefaults/Extensions.cs b/ZeroSpace/ZeroSpace.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000..7c77467 --- /dev/null +++ b/ZeroSpace/ZeroSpace.ServiceDefaults/Extensions.cs @@ -0,0 +1,108 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire service defaults: OpenTelemetry, health checks, resilience, and service discovery. +public static class Extensions +{ + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in other environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks("/health"); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + else + { + app.MapHealthChecks("/health"); + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/ZeroSpace/ZeroSpace.ServiceDefaults/ZeroSpace.ServiceDefaults.csproj b/ZeroSpace/ZeroSpace.ServiceDefaults/ZeroSpace.ServiceDefaults.csproj new file mode 100644 index 0000000..2f453f3 --- /dev/null +++ b/ZeroSpace/ZeroSpace.ServiceDefaults/ZeroSpace.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net10.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/ZS/Web/App.razor b/ZeroSpace/ZeroSpace.Web/App.razor similarity index 100% rename from ZS/Web/App.razor rename to ZeroSpace/ZeroSpace.Web/App.razor diff --git a/ZeroSpace/ZeroSpace.Web/Dockerfile b/ZeroSpace/ZeroSpace.Web/Dockerfile new file mode 100644 index 0000000..c69ccb5 --- /dev/null +++ b/ZeroSpace/ZeroSpace.Web/Dockerfile @@ -0,0 +1,20 @@ +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +WORKDIR /src + +# Copy project files for caching restore layer +COPY ["ZeroSpace.Web/ZeroSpace.Web.csproj", "ZeroSpace.Web/"] +COPY ["ZeroSpace.Model/ZeroSpace.Model.csproj", "ZeroSpace.Model/"] + +RUN dotnet restore "ZeroSpace.Web/ZeroSpace.Web.csproj" + +# Copy source code and build +COPY . . +WORKDIR "/src/ZeroSpace.Web" +RUN dotnet publish "ZeroSpace.Web.csproj" -c Release -o /app/publish + +FROM nginx:alpine AS final +WORKDIR /usr/share/nginx/html +COPY --from=build /app/publish/wwwroot . +COPY ZeroSpace.Web/nginx.conf /etc/nginx/nginx.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/ZS/Web/Layout/MainLayout.razor b/ZeroSpace/ZeroSpace.Web/Layout/MainLayout.razor similarity index 100% rename from ZS/Web/Layout/MainLayout.razor rename to ZeroSpace/ZeroSpace.Web/Layout/MainLayout.razor diff --git a/ZS/Web/Layout/MainLayout.razor.css b/ZeroSpace/ZeroSpace.Web/Layout/MainLayout.razor.css similarity index 100% rename from ZS/Web/Layout/MainLayout.razor.css rename to ZeroSpace/ZeroSpace.Web/Layout/MainLayout.razor.css diff --git a/ZS/Web/Layout/NavMenu.razor b/ZeroSpace/ZeroSpace.Web/Layout/NavMenu.razor similarity index 100% rename from ZS/Web/Layout/NavMenu.razor rename to ZeroSpace/ZeroSpace.Web/Layout/NavMenu.razor diff --git a/ZS/Web/Layout/NavMenu.razor.css b/ZeroSpace/ZeroSpace.Web/Layout/NavMenu.razor.css similarity index 100% rename from ZS/Web/Layout/NavMenu.razor.css rename to ZeroSpace/ZeroSpace.Web/Layout/NavMenu.razor.css diff --git a/ZS/Web/Pages/Home.razor b/ZeroSpace/ZeroSpace.Web/Pages/Home.razor similarity index 100% rename from ZS/Web/Pages/Home.razor rename to ZeroSpace/ZeroSpace.Web/Pages/Home.razor diff --git a/ZS/Web/Pages/NotFound.razor b/ZeroSpace/ZeroSpace.Web/Pages/NotFound.razor similarity index 100% rename from ZS/Web/Pages/NotFound.razor rename to ZeroSpace/ZeroSpace.Web/Pages/NotFound.razor diff --git a/ZS/Web/Pages/Units.razor b/ZeroSpace/ZeroSpace.Web/Pages/Units.razor similarity index 70% rename from ZS/Web/Pages/Units.razor rename to ZeroSpace/ZeroSpace.Web/Pages/Units.razor index 8b49908..eeba544 100644 --- a/ZS/Web/Pages/Units.razor +++ b/ZeroSpace/ZeroSpace.Web/Pages/Units.razor @@ -1,8 +1,9 @@ @page "/units" -@using Model +@using ZeroSpace.Model +@using ZeroSpace.Web.Services @using Telerik.Blazor @using Telerik.Blazor.Components - +@inject IUnitsApiClient UnitsApiClient Units @@ -11,14 +12,29 @@
-@if (gearNotes == null) +@if (isLoading) {
- Loading... + Loading units...
} +else if (errorMessage != null) +{ + +} +else if (gearNotes == null || gearNotes.Count == 0) +{ + +} else {
@@ -54,12 +70,31 @@ else @code { private List? gearNotes; + private bool isLoading = true; + private string? errorMessage; protected override async Task OnInitializedAsync() { - var index = Model.Units.All; - gearNotes = index - .ToList(); + await LoadUnitsAsync(); } + private async Task LoadUnitsAsync() + { + isLoading = true; + errorMessage = null; + + try + { + var units = await UnitsApiClient.GetUnitsAsync(); + gearNotes = units.ToList(); + } + catch (Exception ex) + { + errorMessage = $"Failed to load units: {ex.Message}"; + } + finally + { + isLoading = false; + } + } } diff --git a/ZeroSpace/ZeroSpace.Web/Program.cs b/ZeroSpace/ZeroSpace.Web/Program.cs new file mode 100644 index 0000000..aa5dc46 --- /dev/null +++ b/ZeroSpace/ZeroSpace.Web/Program.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using ZeroSpace.Web; +using ZeroSpace.Web.Services; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); +builder.RootComponents.Add("#app"); +builder.RootComponents.Add("head::after"); + +builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + +builder.Services.AddHttpClient(client => +{ + var apiBaseUrl = builder.Configuration["services:apiservice:https:0"] + ?? builder.Configuration["services:apiservice:http:0"] + ?? builder.Configuration["ApiServiceUrl"] + ?? builder.HostEnvironment.BaseAddress; + + if (!apiBaseUrl.EndsWith('/')) + { + apiBaseUrl += "/"; + } + + client.BaseAddress = new Uri(apiBaseUrl); +}); + +builder.Services.AddTelerikBlazor(); + +await builder.Build().RunAsync(); \ No newline at end of file diff --git a/ZS/Web/Properties/launchSettings.json b/ZeroSpace/ZeroSpace.Web/Properties/launchSettings.json similarity index 100% rename from ZS/Web/Properties/launchSettings.json rename to ZeroSpace/ZeroSpace.Web/Properties/launchSettings.json diff --git a/ZeroSpace/ZeroSpace.Web/Services/IUnitsApiClient.cs b/ZeroSpace/ZeroSpace.Web/Services/IUnitsApiClient.cs new file mode 100644 index 0000000..11b3d27 --- /dev/null +++ b/ZeroSpace/ZeroSpace.Web/Services/IUnitsApiClient.cs @@ -0,0 +1,11 @@ +using ZeroSpace.Model; + +namespace ZeroSpace.Web.Services; + +public interface IUnitsApiClient +{ + Task> GetUnitsAsync(string? faction = null, int? tier = null, CancellationToken cancellationToken = default); + Task GetUnitByIdAsync(string id, CancellationToken cancellationToken = default); + Task CreateUnitAsync(UnitData unit, CancellationToken cancellationToken = default); + Task DeleteUnitAsync(string id, CancellationToken cancellationToken = default); +} diff --git a/ZeroSpace/ZeroSpace.Web/Services/UnitsApiClient.cs b/ZeroSpace/ZeroSpace.Web/Services/UnitsApiClient.cs new file mode 100644 index 0000000..b25ee5b --- /dev/null +++ b/ZeroSpace/ZeroSpace.Web/Services/UnitsApiClient.cs @@ -0,0 +1,278 @@ +using System.Net.Http.Json; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.Extensions.Logging; +using ZeroSpace.Model; + +namespace ZeroSpace.Web.Services; + +public class UnitsApiClient : IUnitsApiClient +{ + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + private static readonly JsonSerializerOptions JsonOptions = new() + { + PropertyNameCaseInsensitive = true + }; + + private const string AllUnitFields = @" + id + name + hexite + flux + supply + productionTime + health + energy + shields + armorRating + movementSpeed + damagePerSecond + attackRange + attributes + tier + faction + hotkey + buildAtSameTime + limit"; + + public UnitsApiClient(HttpClient httpClient, ILogger logger) + { + _httpClient = httpClient; + _logger = logger; + } + + public async Task> GetUnitsAsync(string? faction = null, int? tier = null, CancellationToken cancellationToken = default) + { + try + { + Dictionary? whereFilter = null; + if (!string.IsNullOrWhiteSpace(faction) || tier.HasValue) + { + whereFilter = new Dictionary(); + if (!string.IsNullOrWhiteSpace(faction)) + { + whereFilter["faction"] = new Dictionary { ["eq"] = faction }; + } + if (tier.HasValue) + { + whereFilter["tier"] = new Dictionary { ["eq"] = tier.Value }; + } + } + + var query = $@" + query GetUnits($where: UnitDataFilterInput) {{ + units(where: $where) {{ + {AllUnitFields} + }} + }}"; + + var requestBody = new + { + query, + variables = new + { + where = whereFilter + } + }; + + var response = await _httpClient.PostAsJsonAsync("graphql", requestBody, cancellationToken); + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync>(JsonOptions, cancellationToken); + if (result?.Data?.Units is not null && result.Data.Units.Count > 0) + { + return result.Data.Units; + } + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to load units via GraphQL API service. Falling back to static units list."); + } + + // Fallback to static model data if GraphQL call fails + var fallback = Units.All.AsEnumerable(); + if (!string.IsNullOrWhiteSpace(faction)) + { + fallback = fallback.Where(u => u.Faction.Equals(faction, StringComparison.OrdinalIgnoreCase)); + } + + if (tier.HasValue) + { + fallback = fallback.Where(u => u.Tier == tier.Value); + } + + return fallback.ToList(); + } + + public async Task GetUnitByIdAsync(string id, CancellationToken cancellationToken = default) + { + try + { + var query = $@" + query GetUnitById($id: String!) {{ + unitById(id: $id) {{ + {AllUnitFields} + }} + }}"; + + var requestBody = new + { + query, + variables = new + { + id + } + }; + + var response = await _httpClient.PostAsJsonAsync("graphql", requestBody, cancellationToken); + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync>(JsonOptions, cancellationToken); + if (result?.Data?.UnitById is not null) + { + return result.Data.UnitById; + } + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to get unit '{Id}' via GraphQL API.", id); + } + + return Units.All.FirstOrDefault(u => u.Id == id); + } + + public async Task CreateUnitAsync(UnitData unit, CancellationToken cancellationToken = default) + { + try + { + var mutation = $@" + mutation CreateUnit($input: CreateUnitInput!) {{ + createUnit(input: $input) {{ + {AllUnitFields} + }} + }}"; + + var input = new + { + id = unit.Id, + name = unit.Name, + hexite = unit.Hexite, + flux = unit.Flux, + supply = unit.Supply, + productionTime = unit.ProductionTime, + health = unit.Health, + energy = unit.Energy, + shields = unit.Shields, + armorRating = unit.ArmorRating, + movementSpeed = unit.MovementSpeed, + damagePerSecond = unit.DamagePerSecond, + attackRange = unit.AttackRange, + attributes = unit.Attributes, + tier = unit.Tier, + faction = unit.Faction, + hotkey = unit.Hotkey, + buildAtSameTime = unit.BuildAtSameTime, + limit = unit.Limit + }; + + var requestBody = new + { + query = mutation, + variables = new + { + input + } + }; + + var response = await _httpClient.PostAsJsonAsync("graphql", requestBody, cancellationToken); + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync>(JsonOptions, cancellationToken); + return result?.Data?.CreateUnit; + } + + _logger.LogError("Failed to create unit via GraphQL. Status: {StatusCode}", response.StatusCode); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error creating unit via GraphQL."); + } + + return null; + } + + public async Task DeleteUnitAsync(string id, CancellationToken cancellationToken = default) + { + try + { + var mutation = @" + mutation DeleteUnit($id: String!) { + deleteUnit(id: $id) + }"; + + var requestBody = new + { + query = mutation, + variables = new + { + id + } + }; + + var response = await _httpClient.PostAsJsonAsync("graphql", requestBody, cancellationToken); + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync>(JsonOptions, cancellationToken); + return result?.Data?.DeleteUnit ?? false; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error deleting unit '{Id}' via GraphQL.", id); + } + + return false; + } + + private sealed class GraphQLResponse + { + [JsonPropertyName("data")] + public T? Data { get; set; } + + [JsonPropertyName("errors")] + public List? Errors { get; set; } + } + + private sealed class GraphQLErrorItem + { + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; + } + + private sealed class UnitsQueryResult + { + [JsonPropertyName("units")] + public List? Units { get; set; } + } + + private sealed class UnitByIdQueryResult + { + [JsonPropertyName("unitById")] + public UnitData? UnitById { get; set; } + } + + private sealed class CreateUnitMutationResult + { + [JsonPropertyName("createUnit")] + public UnitData? CreateUnit { get; set; } + } + + private sealed class DeleteUnitMutationResult + { + [JsonPropertyName("deleteUnit")] + public bool DeleteUnit { get; set; } + } +} diff --git a/ZS/Web/Web.csproj b/ZeroSpace/ZeroSpace.Web/ZeroSpace.Web.csproj similarity index 81% rename from ZS/Web/Web.csproj rename to ZeroSpace/ZeroSpace.Web/ZeroSpace.Web.csproj index be1690a..678d606 100644 --- a/ZS/Web/Web.csproj +++ b/ZeroSpace/ZeroSpace.Web/ZeroSpace.Web.csproj @@ -10,11 +10,12 @@ + - + diff --git a/ZS/Web/_Imports.razor b/ZeroSpace/ZeroSpace.Web/_Imports.razor similarity index 82% rename from ZS/Web/_Imports.razor rename to ZeroSpace/ZeroSpace.Web/_Imports.razor index eadfbc1..6f5f0a1 100644 --- a/ZS/Web/_Imports.razor +++ b/ZeroSpace/ZeroSpace.Web/_Imports.razor @@ -6,5 +6,6 @@ @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.Components.WebAssembly.Http @using Microsoft.JSInterop -@using Web -@using Web.Layout \ No newline at end of file +@using ZeroSpace.Web +@using ZeroSpace.Web.Layout +@using ZeroSpace.Model \ No newline at end of file diff --git a/ZeroSpace/ZeroSpace.Web/nginx.conf b/ZeroSpace/ZeroSpace.Web/nginx.conf new file mode 100644 index 0000000..7bd2118 --- /dev/null +++ b/ZeroSpace/ZeroSpace.Web/nginx.conf @@ -0,0 +1,27 @@ +events { } +http { + include mime.types; + types { + application/wasm wasm; + } + default_type application/octet-stream; + + server { + listen 80; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + # Cache static assets + location ~* \.(?:ico|css|js|gif|jpe?g|png|wasm|woff2?|eot|ttf|svg)$ { + root /usr/share/nginx/html; + expires 1y; + add_header Cache-Control "public, max-age=31536000, immutable"; + try_files $uri =404; + } + } +} diff --git a/ZeroSpace/ZeroSpace.Web/wwwroot/appsettings.Development.json b/ZeroSpace/ZeroSpace.Web/wwwroot/appsettings.Development.json new file mode 100644 index 0000000..e0209bf --- /dev/null +++ b/ZeroSpace/ZeroSpace.Web/wwwroot/appsettings.Development.json @@ -0,0 +1,3 @@ +{ + "ApiServiceUrl": "https://localhost:7142" +} diff --git a/ZeroSpace/ZeroSpace.Web/wwwroot/appsettings.json b/ZeroSpace/ZeroSpace.Web/wwwroot/appsettings.json new file mode 100644 index 0000000..e0209bf --- /dev/null +++ b/ZeroSpace/ZeroSpace.Web/wwwroot/appsettings.json @@ -0,0 +1,3 @@ +{ + "ApiServiceUrl": "https://localhost:7142" +} diff --git a/ZS/Web/wwwroot/css/app.css b/ZeroSpace/ZeroSpace.Web/wwwroot/css/app.css similarity index 100% rename from ZS/Web/wwwroot/css/app.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/css/app.css diff --git a/ZS/Web/wwwroot/favicon.png b/ZeroSpace/ZeroSpace.Web/wwwroot/favicon.png similarity index 100% rename from ZS/Web/wwwroot/favicon.png rename to ZeroSpace/ZeroSpace.Web/wwwroot/favicon.png diff --git a/ZS/Web/wwwroot/icon-192.png b/ZeroSpace/ZeroSpace.Web/wwwroot/icon-192.png similarity index 100% rename from ZS/Web/wwwroot/icon-192.png rename to ZeroSpace/ZeroSpace.Web/wwwroot/icon-192.png diff --git a/ZS/Web/wwwroot/index.html b/ZeroSpace/ZeroSpace.Web/wwwroot/index.html similarity index 94% rename from ZS/Web/wwwroot/index.html rename to ZeroSpace/ZeroSpace.Web/wwwroot/index.html index 4137b57..459611f 100644 --- a/ZS/Web/wwwroot/index.html +++ b/ZeroSpace/ZeroSpace.Web/wwwroot/index.html @@ -11,7 +11,7 @@ - + diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js diff --git a/ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map b/ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map similarity index 100% rename from ZS/Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map rename to ZeroSpace/ZeroSpace.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map diff --git a/ZS/Web/wwwroot/sample-data/weather.json b/ZeroSpace/ZeroSpace.Web/wwwroot/sample-data/weather.json similarity index 100% rename from ZS/Web/wwwroot/sample-data/weather.json rename to ZeroSpace/ZeroSpace.Web/wwwroot/sample-data/weather.json diff --git a/ZeroSpace/ZeroSpace.sln b/ZeroSpace/ZeroSpace.sln new file mode 100644 index 0000000..9094213 --- /dev/null +++ b/ZeroSpace/ZeroSpace.sln @@ -0,0 +1,115 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroSpace.Web", "ZeroSpace.Web\ZeroSpace.Web.csproj", "{16296B27-D17B-4BFF-B08F-60F20DF36066}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroSpace.Build", "ZeroSpace.Build\ZeroSpace.Build.csproj", "{C1C04020-AFD6-493B-ABDB-277977D3488E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroSpace.Model", "ZeroSpace.Model\ZeroSpace.Model.csproj", "{E31A06B0-9B2B-4805-B343-5687A5992E9A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroSpace.Deploy", "ZeroSpace.Deploy\ZeroSpace.Deploy.csproj", "{DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroSpace.ServiceDefaults", "ZeroSpace.ServiceDefaults\ZeroSpace.ServiceDefaults.csproj", "{8F122E45-E709-43B1-B8E5-362E59F83A7C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroSpace.AppHost", "ZeroSpace.AppHost\ZeroSpace.AppHost.csproj", "{9E7B86A0-C848-478A-A808-A1C3D633FD76}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroSpace.ApiService", "ZeroSpace.ApiService\ZeroSpace.ApiService.csproj", "{8876FC23-FEA8-46A7-A10E-299EAC69A392}" +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 + {16296B27-D17B-4BFF-B08F-60F20DF36066}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16296B27-D17B-4BFF-B08F-60F20DF36066}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16296B27-D17B-4BFF-B08F-60F20DF36066}.Debug|x64.ActiveCfg = Debug|Any CPU + {16296B27-D17B-4BFF-B08F-60F20DF36066}.Debug|x64.Build.0 = Debug|Any CPU + {16296B27-D17B-4BFF-B08F-60F20DF36066}.Debug|x86.ActiveCfg = Debug|Any CPU + {16296B27-D17B-4BFF-B08F-60F20DF36066}.Debug|x86.Build.0 = Debug|Any CPU + {16296B27-D17B-4BFF-B08F-60F20DF36066}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16296B27-D17B-4BFF-B08F-60F20DF36066}.Release|Any CPU.Build.0 = Release|Any CPU + {16296B27-D17B-4BFF-B08F-60F20DF36066}.Release|x64.ActiveCfg = Release|Any CPU + {16296B27-D17B-4BFF-B08F-60F20DF36066}.Release|x64.Build.0 = Release|Any CPU + {16296B27-D17B-4BFF-B08F-60F20DF36066}.Release|x86.ActiveCfg = Release|Any CPU + {16296B27-D17B-4BFF-B08F-60F20DF36066}.Release|x86.Build.0 = Release|Any CPU + {C1C04020-AFD6-493B-ABDB-277977D3488E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C1C04020-AFD6-493B-ABDB-277977D3488E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C1C04020-AFD6-493B-ABDB-277977D3488E}.Debug|x64.ActiveCfg = Debug|Any CPU + {C1C04020-AFD6-493B-ABDB-277977D3488E}.Debug|x64.Build.0 = Debug|Any CPU + {C1C04020-AFD6-493B-ABDB-277977D3488E}.Debug|x86.ActiveCfg = Debug|Any CPU + {C1C04020-AFD6-493B-ABDB-277977D3488E}.Debug|x86.Build.0 = Debug|Any CPU + {C1C04020-AFD6-493B-ABDB-277977D3488E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C1C04020-AFD6-493B-ABDB-277977D3488E}.Release|Any CPU.Build.0 = Release|Any CPU + {C1C04020-AFD6-493B-ABDB-277977D3488E}.Release|x64.ActiveCfg = Release|Any CPU + {C1C04020-AFD6-493B-ABDB-277977D3488E}.Release|x64.Build.0 = Release|Any CPU + {C1C04020-AFD6-493B-ABDB-277977D3488E}.Release|x86.ActiveCfg = Release|Any CPU + {C1C04020-AFD6-493B-ABDB-277977D3488E}.Release|x86.Build.0 = Release|Any CPU + {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Debug|x64.ActiveCfg = Debug|Any CPU + {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Debug|x64.Build.0 = Debug|Any CPU + {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Debug|x86.ActiveCfg = Debug|Any CPU + {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Debug|x86.Build.0 = Debug|Any CPU + {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Release|Any CPU.Build.0 = Release|Any CPU + {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Release|x64.ActiveCfg = Release|Any CPU + {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Release|x64.Build.0 = Release|Any CPU + {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Release|x86.ActiveCfg = Release|Any CPU + {E31A06B0-9B2B-4805-B343-5687A5992E9A}.Release|x86.Build.0 = Release|Any CPU + {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Debug|x64.ActiveCfg = Debug|Any CPU + {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Debug|x64.Build.0 = Debug|Any CPU + {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Debug|x86.ActiveCfg = Debug|Any CPU + {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Debug|x86.Build.0 = Debug|Any CPU + {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Release|Any CPU.Build.0 = Release|Any CPU + {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Release|x64.ActiveCfg = Release|Any CPU + {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Release|x64.Build.0 = Release|Any CPU + {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Release|x86.ActiveCfg = Release|Any CPU + {DECE54F2-51BB-4D7D-BD20-BE7F1C298FA3}.Release|x86.Build.0 = Release|Any CPU + {8F122E45-E709-43B1-B8E5-362E59F83A7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F122E45-E709-43B1-B8E5-362E59F83A7C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F122E45-E709-43B1-B8E5-362E59F83A7C}.Debug|x64.ActiveCfg = Debug|Any CPU + {8F122E45-E709-43B1-B8E5-362E59F83A7C}.Debug|x64.Build.0 = Debug|Any CPU + {8F122E45-E709-43B1-B8E5-362E59F83A7C}.Debug|x86.ActiveCfg = Debug|Any CPU + {8F122E45-E709-43B1-B8E5-362E59F83A7C}.Debug|x86.Build.0 = Debug|Any CPU + {8F122E45-E709-43B1-B8E5-362E59F83A7C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F122E45-E709-43B1-B8E5-362E59F83A7C}.Release|Any CPU.Build.0 = Release|Any CPU + {8F122E45-E709-43B1-B8E5-362E59F83A7C}.Release|x64.ActiveCfg = Release|Any CPU + {8F122E45-E709-43B1-B8E5-362E59F83A7C}.Release|x64.Build.0 = Release|Any CPU + {8F122E45-E709-43B1-B8E5-362E59F83A7C}.Release|x86.ActiveCfg = Release|Any CPU + {8F122E45-E709-43B1-B8E5-362E59F83A7C}.Release|x86.Build.0 = Release|Any CPU + {9E7B86A0-C848-478A-A808-A1C3D633FD76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E7B86A0-C848-478A-A808-A1C3D633FD76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E7B86A0-C848-478A-A808-A1C3D633FD76}.Debug|x64.ActiveCfg = Debug|Any CPU + {9E7B86A0-C848-478A-A808-A1C3D633FD76}.Debug|x64.Build.0 = Debug|Any CPU + {9E7B86A0-C848-478A-A808-A1C3D633FD76}.Debug|x86.ActiveCfg = Debug|Any CPU + {9E7B86A0-C848-478A-A808-A1C3D633FD76}.Debug|x86.Build.0 = Debug|Any CPU + {9E7B86A0-C848-478A-A808-A1C3D633FD76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E7B86A0-C848-478A-A808-A1C3D633FD76}.Release|Any CPU.Build.0 = Release|Any CPU + {9E7B86A0-C848-478A-A808-A1C3D633FD76}.Release|x64.ActiveCfg = Release|Any CPU + {9E7B86A0-C848-478A-A808-A1C3D633FD76}.Release|x64.Build.0 = Release|Any CPU + {9E7B86A0-C848-478A-A808-A1C3D633FD76}.Release|x86.ActiveCfg = Release|Any CPU + {9E7B86A0-C848-478A-A808-A1C3D633FD76}.Release|x86.Build.0 = Release|Any CPU + {8876FC23-FEA8-46A7-A10E-299EAC69A392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8876FC23-FEA8-46A7-A10E-299EAC69A392}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8876FC23-FEA8-46A7-A10E-299EAC69A392}.Debug|x64.ActiveCfg = Debug|Any CPU + {8876FC23-FEA8-46A7-A10E-299EAC69A392}.Debug|x64.Build.0 = Debug|Any CPU + {8876FC23-FEA8-46A7-A10E-299EAC69A392}.Debug|x86.ActiveCfg = Debug|Any CPU + {8876FC23-FEA8-46A7-A10E-299EAC69A392}.Debug|x86.Build.0 = Debug|Any CPU + {8876FC23-FEA8-46A7-A10E-299EAC69A392}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8876FC23-FEA8-46A7-A10E-299EAC69A392}.Release|Any CPU.Build.0 = Release|Any CPU + {8876FC23-FEA8-46A7-A10E-299EAC69A392}.Release|x64.ActiveCfg = Release|Any CPU + {8876FC23-FEA8-46A7-A10E-299EAC69A392}.Release|x64.Build.0 = Release|Any CPU + {8876FC23-FEA8-46A7-A10E-299EAC69A392}.Release|x86.ActiveCfg = Release|Any CPU + {8876FC23-FEA8-46A7-A10E-299EAC69A392}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ZeroSpace/aspirate.json b/ZeroSpace/aspirate.json new file mode 100644 index 0000000..6bcf779 --- /dev/null +++ b/ZeroSpace/aspirate.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://raw.githubusercontent.com/prom3theu5/aspirate/main/src/Aspirate/aspirate-schema.json", + "projectPath": "ZeroSpace.AppHost/ZeroSpace.AppHost.csproj", + "outputFormat": "kustomize", + "outputPath": "k8s/aspirate-output", + "containerRegistry": "docker.io/zerospace", + "containerImageTags": [ + "latest" + ], + "secrets": { + "salt": "zerospace-aspirate-salt", + "password": "zerospace-aspirate-password" + } +} diff --git a/ZeroSpace/docker-compose.yml b/ZeroSpace/docker-compose.yml new file mode 100644 index 0000000..9df2287 --- /dev/null +++ b/ZeroSpace/docker-compose.yml @@ -0,0 +1,49 @@ +version: '3.8' + +services: + postgres: + image: postgres:17-alpine + container_name: zerospace-postgres + restart: unless-stopped + environment: + POSTGRES_DB: zerospacedb + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password123 + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d zerospacedb"] + interval: 5s + timeout: 5s + retries: 5 + + apiservice: + build: + context: . + dockerfile: ZeroSpace.ApiService/Dockerfile + container_name: zerospace-apiservice + restart: unless-stopped + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ConnectionStrings__zerospacedb=Host=postgres;Port=5432;Database=zerospacedb;Username=postgres;Password=password123 + ports: + - "5142:8080" + depends_on: + postgres: + condition: service_healthy + + web: + build: + context: . + dockerfile: ZeroSpace.Web/Dockerfile + container_name: zerospace-web + restart: unless-stopped + ports: + - "8080:80" + depends_on: + - apiservice + +volumes: + postgres_data: diff --git a/ZeroSpace/k8s/apiservice-deployment.yaml b/ZeroSpace/k8s/apiservice-deployment.yaml new file mode 100644 index 0000000..4a60829 --- /dev/null +++ b/ZeroSpace/k8s/apiservice-deployment.yaml @@ -0,0 +1,59 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: apiservice + labels: + app.kubernetes.io/name: apiservice + app.kubernetes.io/part-of: zerospace +spec: + replicas: 2 + selector: + matchLabels: + app: apiservice + template: + metadata: + labels: + app: apiservice + app.kubernetes.io/name: apiservice + app.kubernetes.io/part-of: zerospace + spec: + containers: + - name: apiservice + image: zerospace-apiservice:latest + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 8080 + env: + - name: ASPNETCORE_ENVIRONMENT + value: Production + - name: ConnectionStrings__zerospacedb + valueFrom: + secretKeyRef: + name: postgres-secret + key: connection-string + - name: OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES + value: "true" + - name: OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES + value: "true" + livenessProbe: + httpGet: + path: /alive + port: 8080 + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi diff --git a/ZeroSpace/k8s/apiservice-service.yaml b/ZeroSpace/k8s/apiservice-service.yaml new file mode 100644 index 0000000..cbb378a --- /dev/null +++ b/ZeroSpace/k8s/apiservice-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: apiservice + labels: + app.kubernetes.io/name: apiservice + app.kubernetes.io/part-of: zerospace +spec: + type: ClusterIP + ports: + - name: http + port: 8080 + targetPort: 8080 + selector: + app: apiservice diff --git a/ZeroSpace/k8s/ingress.yaml b/ZeroSpace/k8s/ingress.yaml new file mode 100644 index 0000000..1976a18 --- /dev/null +++ b/ZeroSpace/k8s/ingress.yaml @@ -0,0 +1,35 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: zerospace-ingress + labels: + app.kubernetes.io/part-of: zerospace + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "false" + nginx.ingress.kubernetes.io/use-regex: "true" +spec: + ingressClassName: nginx + rules: + - http: + paths: + - path: /graphql(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: apiservice + port: + number: 8080 + - path: /api(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: apiservice + port: + number: 8080 + - path: /(.*) + pathType: ImplementationSpecific + backend: + service: + name: web + port: + number: 80 diff --git a/ZeroSpace/k8s/kustomization.yaml b/ZeroSpace/k8s/kustomization.yaml new file mode 100644 index 0000000..af99fab --- /dev/null +++ b/ZeroSpace/k8s/kustomization.yaml @@ -0,0 +1,16 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - postgres-secret.yaml + - postgres-pvc.yaml + - postgres-deployment.yaml + - postgres-service.yaml + - apiservice-deployment.yaml + - apiservice-service.yaml + - web-deployment.yaml + - web-service.yaml + - ingress.yaml + +commonLabels: + app.kubernetes.io/part-of: zerospace diff --git a/ZeroSpace/k8s/postgres-deployment.yaml b/ZeroSpace/k8s/postgres-deployment.yaml new file mode 100644 index 0000000..02431aa --- /dev/null +++ b/ZeroSpace/k8s/postgres-deployment.yaml @@ -0,0 +1,63 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres + labels: + app.kubernetes.io/name: postgres + app.kubernetes.io/part-of: zerospace +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + app.kubernetes.io/name: postgres + app.kubernetes.io/part-of: zerospace + spec: + containers: + - name: postgres + image: postgres:17-alpine + imagePullPolicy: IfNotPresent + ports: + - name: tcp-postgres + containerPort: 5432 + envFrom: + - secretRef: + name: postgres-secret + volumeMounts: + - name: postgres-storage + mountPath: /var/lib/postgresql/data + livenessProbe: + exec: + command: + - /bin/sh + - -c + - exec pg_isready -U postgres -d zerospacedb + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 5 + readinessProbe: + exec: + command: + - /bin/sh + - -c + - exec pg_isready -U postgres -d zerospacedb + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + volumes: + - name: postgres-storage + persistentVolumeClaim: + claimName: postgres-data-pvc diff --git a/ZeroSpace/k8s/postgres-pvc.yaml b/ZeroSpace/k8s/postgres-pvc.yaml new file mode 100644 index 0000000..f122162 --- /dev/null +++ b/ZeroSpace/k8s/postgres-pvc.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-data-pvc + labels: + app.kubernetes.io/name: postgres + app.kubernetes.io/part-of: zerospace +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi diff --git a/ZeroSpace/k8s/postgres-secret.yaml b/ZeroSpace/k8s/postgres-secret.yaml new file mode 100644 index 0000000..14c985c --- /dev/null +++ b/ZeroSpace/k8s/postgres-secret.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Secret +metadata: + name: postgres-secret + labels: + app.kubernetes.io/name: postgres + app.kubernetes.io/part-of: zerospace +type: Opaque +stringData: + POSTGRES_DB: zerospacedb + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password123 + connection-string: "Host=postgres;Port=5432;Database=zerospacedb;Username=postgres;Password=password123" diff --git a/ZeroSpace/k8s/postgres-service.yaml b/ZeroSpace/k8s/postgres-service.yaml new file mode 100644 index 0000000..1e06059 --- /dev/null +++ b/ZeroSpace/k8s/postgres-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: postgres + labels: + app.kubernetes.io/name: postgres + app.kubernetes.io/part-of: zerospace +spec: + type: ClusterIP + ports: + - name: tcp-postgres + port: 5432 + targetPort: 5432 + selector: + app: postgres diff --git a/ZeroSpace/k8s/web-deployment.yaml b/ZeroSpace/k8s/web-deployment.yaml new file mode 100644 index 0000000..1796967 --- /dev/null +++ b/ZeroSpace/k8s/web-deployment.yaml @@ -0,0 +1,47 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: web + labels: + app.kubernetes.io/name: web + app.kubernetes.io/part-of: zerospace +spec: + replicas: 2 + selector: + matchLabels: + app: web + template: + metadata: + labels: + app: web + app.kubernetes.io/name: web + app.kubernetes.io/part-of: zerospace + spec: + containers: + - name: web + image: zerospace-web:latest + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 80 + livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + readinessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 3 + periodSeconds: 5 + timeoutSeconds: 3 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 250m + memory: 256Mi diff --git a/ZeroSpace/k8s/web-service.yaml b/ZeroSpace/k8s/web-service.yaml new file mode 100644 index 0000000..6a069ab --- /dev/null +++ b/ZeroSpace/k8s/web-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: web + labels: + app.kubernetes.io/name: web + app.kubernetes.io/part-of: zerospace +spec: + type: ClusterIP + ports: + - name: http + port: 80 + targetPort: 80 + selector: + app: web