From 492859f17f7be3a6f6800d4c02787a0e48c9df8e Mon Sep 17 00:00:00 2001 From: claude Date: Tue, 16 Jun 2026 22:48:38 +0300 Subject: [PATCH 1/9] ci: prune images + build cache after build (prevent runner disk exhaustion) Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/build.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 850a436..320cea5 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -97,4 +97,10 @@ jobs: - name: Push Page Fetcher API image run: | - docker push "${REGISTRY_HOST}/${PAGE_FETCHER_API_IMAGE}:${IMAGE_TAG}" \ No newline at end of file + docker push "${REGISTRY_HOST}/${PAGE_FETCHER_API_IMAGE}:${IMAGE_TAG}" + + - name: Reclaim disk space + if: always() + run: | + docker image prune -af + docker builder prune -af \ No newline at end of file From 2192c3f4c507947521796c3db8a1746f07025f92 Mon Sep 17 00:00:00 2001 From: claude Date: Wed, 17 Jun 2026 11:03:55 +0300 Subject: [PATCH 2/9] Logs: Compact JSON + aligned enrichment in shared StartupExtensions CompactJsonFormatter in both ConfigureJsonSerilog overloads; rename Service->Application, EnvironmentName->Environment (keep AppVersion). Applies to all myAi services. Co-Authored-By: Claude Opus 4.8 --- Directory.Packages.props | 1 + Helpers/startup-helpers/StartupExtensions.cs | 14 ++++++-------- Helpers/startup-helpers/startup-helpers.csproj | 1 + 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 7d04f04..817eda5 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -23,6 +23,7 @@ + diff --git a/Helpers/startup-helpers/StartupExtensions.cs b/Helpers/startup-helpers/StartupExtensions.cs index c806e1b..ce55b4d 100644 --- a/Helpers/startup-helpers/StartupExtensions.cs +++ b/Helpers/startup-helpers/StartupExtensions.cs @@ -39,11 +39,10 @@ public static class StartupExtensions .ReadFrom.Configuration(context.Configuration) .ReadFrom.Services(services) .Enrich.FromLogContext() - .Enrich.WithMachineName() - .Enrich.WithEnvironmentName() - .Enrich.WithProperty("Service", serviceName) + .Enrich.WithProperty("Application", serviceName) + .Enrich.WithProperty("Environment", context.HostingEnvironment.EnvironmentName) .Enrich.WithProperty("AppVersion", appVersion) - .WriteTo.Console(new Serilog.Formatting.Json.JsonFormatter()); + .WriteTo.Console(new Serilog.Formatting.Compact.CompactJsonFormatter()); AddEmailSinkIfConfigured(configuration, context.Configuration, serviceName); }); @@ -57,11 +56,10 @@ public static class StartupExtensions .ReadFrom.Configuration(builder.Configuration) .ReadFrom.Services(services) .Enrich.FromLogContext() - .Enrich.WithMachineName() - .Enrich.WithEnvironmentName() - .Enrich.WithProperty("Service", serviceName) + .Enrich.WithProperty("Application", serviceName) + .Enrich.WithProperty("Environment", builder.Environment.EnvironmentName) .Enrich.WithProperty("AppVersion", appVersion) - .WriteTo.Console(new Serilog.Formatting.Json.JsonFormatter()); + .WriteTo.Console(new Serilog.Formatting.Compact.CompactJsonFormatter()); AddEmailSinkIfConfigured(configuration, builder.Configuration, serviceName); }); diff --git a/Helpers/startup-helpers/startup-helpers.csproj b/Helpers/startup-helpers/startup-helpers.csproj index 89d7a7c..0563262 100644 --- a/Helpers/startup-helpers/startup-helpers.csproj +++ b/Helpers/startup-helpers/startup-helpers.csproj @@ -17,6 +17,7 @@ + From 2d5572725d79994dd463ce5a98b6161ff809ba92 Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 18 Jun 2026 15:46:46 +0300 Subject: [PATCH 3/9] docs: add Observability section to CLAUDE.md (Compact JSON logs->Loki); gitignore ACCESS.md Co-Authored-By: Claude Opus 4.8 --- .gitignore | 3 +++ CLAUDE.md | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b4be5a0..24928ba 100644 --- a/.gitignore +++ b/.gitignore @@ -376,3 +376,6 @@ files/ /docker-compose/.env.production /docker-compose/.env.staging + +# local infra access notes (secrets) — never commit +ACCESS.md diff --git a/CLAUDE.md b/CLAUDE.md index a7786c6..0b8bc91 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,11 +34,15 @@ This applies to both the staging and production repos as appropriate. - .NET 10, ASP.NET Core, Worker Service - Entity Framework Core + SQL Server (multi-schema) - Refit for typed HTTP clients between services -- Serilog (JSON structured logging, Console + File + Email sinks) +- Serilog — Compact JSON logs to stdout (+ optional email sink); see Observability - MailKit for SMTP (used exclusively in `email-api`) - Docker Compose for local and production deployment - Watchtower for automatic container updates in production +## Observability (central stack on monitoring host 10.0.0.156) +- **Logs**: every service uses `ConfigureJsonSerilog(ServiceName, appVersion)` (startup-helpers) → Serilog **Compact JSON** to stdout, enriched `Application`/`Environment`/`AppVersion`. The host's Grafana **Alloy** agent ships stdout → **Loki**; view/query in Grafana. No file sink; optional email sink only if `SerilogEmail:*` is configured. +- **No app metrics/traces** — these are simple/minimal services, so (unlike easyDent) they don't expose Prometheus metrics or OTLP traces. Container/host metrics still come from the host's cAdvisor/node_exporter. + ## Project taxonomy | Category | Naming | Contains | EF dependency | From 9b33876c110f180d5fe05976dd121aeec538acc5 Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 18 Jun 2026 16:04:23 +0300 Subject: [PATCH 4/9] docs: replace template README with a proper one (intro, run, deploy, logging) Co-Authored-By: Claude Opus 4.8 --- README.md | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index e37e4b1..07b1666 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,27 @@ -# Introduction -TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project. +# myAi -# Getting Started -TODO: Guide users through getting your code up and running on their own system. In this section you can talk about: -1. Installation process -2. Software dependencies -3. Latest releases -4. API references +The **myai.ro** platform — a set of .NET microservices (CV matching, RAG, email, CV search, page +fetching, …) behind a web frontend + API. Part of the easySoft platform. -# Build and Test -TODO: Describe and show how to build your code and run the tests. +## Layout +Multiple services (`*-api`, `*-job`) + `web`, sharing a common bootstrap in +`startup-helpers/` (Serilog, Swagger, `.env`/Key Vault loading, middleware). See **CLAUDE.md** +for the full service map, dependency chain, and conventions. -# Contribute -TODO: Explain how other users and developers can contribute to make your code better. +## Run locally +```bash +docker compose up --build # or run individual services with: dotnet run --project +``` -If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files: -- [ASP.NET Core](https://github.com/aspnet/Home) -- [Visual Studio Code](https://github.com/Microsoft/vscode) -- [Chakra Core](https://github.com/Microsoft/ChakraCore) \ No newline at end of file +## Deploy +CI builds `registry.easysoft.ro/apps/myai-*:{staging,production}`; Watchtower rolls them out to +the **staging (`10.0.0.183`)** + **production (`10.0.0.248`)** Portainer stacks. Edge Caddy serves +**myai.ro** (prod) / **myai.easysoft.ro** (staging). + +## Logging +Every service: `ConfigureJsonSerilog(name, version)` → Serilog **Compact JSON** to stdout → Grafana +**Alloy** → **Loki**. No app metrics/traces (simple services). + +--- +See **CLAUDE.md** for the detailed solution guide and **ACCESS.md** (local, gitignored) for +infrastructure access. From c5e1b7f687a1a71b54b378d20f25e7cf8ee92cf9 Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 18 Jun 2026 16:29:07 +0300 Subject: [PATCH 5/9] ci: branch-driven deploys (staging/production branches), build the pushed commit main = day-to-day work (no deploy). Merge into staging -> :staging, into production -> :production; IMAGE_TAG = branch name. Also fixes the checkout to build the PUSHED commit (git checkout $GITHUB_SHA) instead of always cloning the default branch. Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/build.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 320cea5..e46468c 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -1,9 +1,14 @@ -name: Build and Push Docker Images Staging +name: Build and Push Docker Images +# Branch-driven deploys — no yaml edits to switch environment: +# merge into `staging` -> tag :staging (staging Watchtower deploys) +# merge into `production` -> tag :production (production Watchtower deploys) +# `main` is the day-to-day work branch and deploys nothing. on: push: branches: - staging + - production env: GIT_HOST: git.easysoft.ro @@ -16,18 +21,19 @@ env: CV_CLEANUP_JOB_IMAGE: apps/myai-cv-cleanup-job CV_SEARCH_JOB_IMAGE: apps/myai-cv-search-job PAGE_FETCHER_API_IMAGE: apps/myai-page-fetcher-api - IMAGE_TAG: staging + IMAGE_TAG: ${{ github.ref_name }} # branch name == image tag (staging | production) jobs: build: runs-on: host steps: - - name: Checkout repository + - name: Checkout the pushed commit env: TOKEN: ${{ secrets.REPO_TOKEN }} run: | git clone "http://gelu:${TOKEN}@${GIT_HOST}:3000/${GITHUB_REPOSITORY}.git" . + git checkout "${{ github.sha }}" - name: Login to registry run: | From 903fbcd143630960ec2bfffdd56f67420bb4869f Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 18 Jun 2026 17:36:59 +0300 Subject: [PATCH 6/9] ci: preserve Docker build cache (faster runner) Stop wiping the layer cache + base images after every build (the host has 86GB free). Keep base images (prune dangling only) and ~2 weeks of build cache, so the cache-friendly Dockerfiles (COPY *.csproj + restore before source) actually benefit -> warm rebuilds skip restore and base-image pulls. Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index e46468c..202332d 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -105,8 +105,8 @@ jobs: run: | docker push "${REGISTRY_HOST}/${PAGE_FETCHER_API_IMAGE}:${IMAGE_TAG}" - - name: Reclaim disk space + - name: Reclaim disk space (keep recent build cache) if: always() run: | - docker image prune -af - docker builder prune -af \ No newline at end of file + docker image prune -f # dangling only (keep base images) + docker builder prune -f --filter until=336h # keep ~2 weeks of layer cache \ No newline at end of file From 27f4cfe21e7215af38e3f66bf778e29997c2cd9d Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 18 Jun 2026 17:58:00 +0300 Subject: [PATCH 7/9] ci: enable BuildKit + let its GC manage the layer cache DOCKER_BUILDKIT=1 (explicit) so the restore layer is cached across builds; drop the explicit 'docker builder prune' (it was wiping that cache) and rely on BuildKit's own GC + 86GB headroom. Cleanup keeps dangling-image removal only. Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 202332d..20513db 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -13,6 +13,7 @@ on: env: GIT_HOST: git.easysoft.ro REGISTRY_HOST: registry.easysoft.ro + DOCKER_BUILDKIT: "1" API_IMAGE: apps/myai-api CV_MATCHER_API_IMAGE: apps/myai-cv-matcher-api RAG_API_IMAGE: apps/myai-rag-api @@ -109,4 +110,3 @@ jobs: if: always() run: | docker image prune -f # dangling only (keep base images) - docker builder prune -f --filter until=336h # keep ~2 weeks of layer cache \ No newline at end of file From 7da084c17431fa6ce39c95ce14c6951ef60dc074 Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 18 Jun 2026 18:02:29 +0300 Subject: [PATCH 8/9] ci: revert DOCKER_BUILDKIT (runner job env has no buildx plugin -> build failed) Keep the cache win (no base-image nuke); back to the legacy builder which the job context supports. BuildKit needs buildx installed in the runner before it can be used. Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 20513db..1b34806 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -13,7 +13,6 @@ on: env: GIT_HOST: git.easysoft.ro REGISTRY_HOST: registry.easysoft.ro - DOCKER_BUILDKIT: "1" API_IMAGE: apps/myai-api CV_MATCHER_API_IMAGE: apps/myai-cv-matcher-api RAG_API_IMAGE: apps/myai-rag-api From 62654978afd1342b1e1b5c20e0187e2921678390 Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 18 Jun 2026 18:09:35 +0300 Subject: [PATCH 9/9] ci: re-enable BuildKit (runner now has buildx plugin) Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 1b34806..20513db 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -13,6 +13,7 @@ on: env: GIT_HOST: git.easysoft.ro REGISTRY_HOST: registry.easysoft.ro + DOCKER_BUILDKIT: "1" API_IMAGE: apps/myai-api CV_MATCHER_API_IMAGE: apps/myai-cv-matcher-api RAG_API_IMAGE: apps/myai-rag-api