Staging to Production #51
Reference in New Issue
Block a user
Delete Branch "main"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
All features verified on staging.
- docker-compose.yml is now the single file for Portainer (staging and prod). Uses registry images with ${IMAGE_TAG:-staging}, ${LOGS_PATH:-/opt/myai/logs}, and ${FILES_PATH:-/opt/myai/files} so the same file works for all environments. - docker-compose.override.yml adds build context, ports, and env_file for local dev and is auto-merged by "docker compose up" (no extra flags needed). - .env.template documents IMAGE_TAG, LOGS_PATH, FILES_PATH alongside existing vars. - docker-compose.dcproj updated so override file nests under docker-compose.yml in Solution Explorer. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>Phases 1-10 of the planned refactoring: Phase 1: rename shared-models -> common - namespace Shared.Models -> Common throughout - remove stale AspNetCore.Http.Features 5.0 reference Phase 2: create shared-data with abstract BaseEntity - BaseEntity: required string Id { get; init; } + DateTime CreatedAt { get; init; } Phase 3: rename myai-models -> myai-data - namespace MyAi.Models -> MyAi.Data - MigrationsAssembly("myai-data") Phase 4: rename cv-search-models -> cv-search-data - namespace CvSearch.Models -> CvSearch.Data - move JobSearchSettings to cv-matcher-api-models - JobSearch*Entity now inherits BaseEntity Phase 5: extract rag-data from rag-api - new project: Apis/rag-data with RagDbContext + entities + migrations - RagDocumentEntity inherits BaseEntity; cache entities use CacheKey PK - fix duplicate AddHttpClient<RagAiClient>/AddScoped registrations in rag-api - MigrationsAssembly("rag-data") Phase 6: extract cv-matcher-data from cv-matcher-api - new project: Apis/cv-matcher-data with CvMatcherDbContext + entities + migrations - CvMatchResultEntity inherits BaseEntity; CvMatcherChatCacheEntity uses CacheKey PK - MigrationsAssembly("cv-matcher-data") Phase 7: create empty cv-cleanup-job-models and cv-search-job-models Phase 8: update all 5 Dockerfiles for renamed/new projects Phase 9: reorganise .sln virtual folders (Apis/Jobs/Models/Data/Helpers) - update root CLAUDE.md with new project taxonomy and migration commands - update cv-matcher-api/CLAUDE.md and cv-search-job/CLAUDE.md Phase 10: add Directory.Packages.props for centralised NuGet versions - remove Version= from all PackageReference elements in active .csproj files No database changes. No runtime behaviour changes. All MigrationId strings in __EFMigrationsHistory are unaffected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>- Add ProjectReference to email-api-data - Register EmailApiDbContext (no migrate — email-api owns migrations) - Register IEmailTemplateRepository (scoped) and IEmailTemplateService (singleton) - EmailApiEmailSender: replace ITemplateService with IEmailTemplateService for all email.* template rendering (match body/subject/footer) - SendMatchAsync: replace _contact.ToEmail operator copy with GetOperatorCopy("email.match.subject", "en") from DB template Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>- Add ProjectReference to email-api-data; remove myai-data reference - Program.cs: register EmailApiDbContext (no migrate), IEmailTemplateRepository (scoped), IEmailTemplateService (singleton); remove MyAiDbContext + ITemplateService registrations and their migration call - CvSearchEmailSender: inject IEmailTemplateService; replace _config["Contact:ToEmail"] with GetOperatorCopy("email.search-results.subject") for operator copy logic; remove IConfiguration injection - docker-compose: remove Contact__ToEmail from cv-search-job service block; add Database__* env vars to email-api service (needed for EmailApiDbContext) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>- Add section block comments throughout for navigability - Merge two duplicate @media (max-width:900px) blocks into one - Remove dead .ai-mark rule (replaced by <img> logo, never rendered) - Move .cookie-overlay, .cookie-manage to display:none by default (removes need for inline style="display:none;" on those elements) - Add .loader-overlay.loader-visible{display:flex} so JS can use class toggling instead of .css('display','flex') — works correctly with jQuery 4.0's getComputedStyle-based visibility detection - Consolidate brand-mark, console-line, nav-actions rules next to their related blocks (were scattered at end of file) Closes #31 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>- Remove all 7 inline style="display:none;" attributes from loaders/cookie elements (now handled by CSS with .loader-overlay { display: none } and .loader-overlay.loader-visible { display: flex }) - Remove orphan footer-legal class (unused in CSS) from footer-links divs - Add <script src="/js/i18n.js"></script> before main.js on both pages - Add <script src="/js/cv-matcher.js"></script> after main.js on cv-matcher page jQuery 4.0 now detects CSS display:none correctly via getComputedStyle, so class-based visibility (.loader-visible) works seamlessly. Closes #31 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>- Create SeedEmailTemplates migration (20260528130652) with all email templates - Add Microsoft.EntityFrameworkCore.Design to email-api.csproj for EF migrations - Add EmailApiDbContext registration and migration support to email-api Program.cs - Configure IEmailTemplateRepository and IEmailTemplateService in email-api - All 14 email templates now seeded in emailApi schema (HTML shells, CV match, job search) - Templates include proper placeholder support ({{score}}, {{count}}, {{jobLabel}}, etc.) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>Refactored cv-matcher.js from 257 → 213 lines (17% reduction) by: - Removing duplicate helper functions now in utils/form-helpers.js - Removing duplicate i18n logic now in utils/i18n.js - Removing API loading code now in utils/api.js Kept CV matcher-specific logic: - CV file input change handler - Async CV upload and match flow with two captcha tokens - Match result rendering with score badge and lists - escapeHtml() XSS prevention utility - $(window).on('load') to load reCaptcha All function calls updated to use window.MyAi.* utilities for consistency. Added detailed JSDoc comments explaining the two-step async flow. Updated cv-matcher/index.html to load all utilities before cv-matcher.js. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>string.Join("") produced no whitespace between inline-block spans, causing keywords to visually merge in email clients that collapse margins. Switched to string.Join(" ") and zeroed left margin on each badge so they wrap cleanly without a gap on the first item. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>Individual job listings on bestjobs.eu use /loc-de-munca/{slug} URLs. The seeded JobLinkContains value /ro/locuri-de-munca/ matched only the category navigation links (Vanzari, Inginerie, Management...), so zero job URLs passed the stage-1 href filter and the scraper returned nothing. Migration updates the stored record (Id=2) to /loc-de-munca/. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>Added seeding of the ai.cv-match.system-prompt to the AiPrompts table. This prompt is retrieved by CvMatcherService when scoring CV-job pairs with the LLM. The {{languageName}} placeholder is substituted at runtime based on the requested language. The prompt has a fallback in the service code, but seeding it ensures the proper version is used and avoids relying on the hardcoded fallback. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>Refactored the AI prompt system to use proper language-specific prompts (en and ro) instead of a single wildcard prompt with runtime {{languageName}} placeholder substitution. Benefits: - Language-specific instructions optimized for each language - Better control over LLM behavior per language - Cleaner code without placeholder substitution - Easier to maintain and update prompts per language Changes: - Updated cvMatcher InitialSchema migration to seed en and ro prompts separately - Modified CvMatcherService to retrieve language-specific prompts directly - Removed LanguageName() helper method (no longer needed) - Added fallback prompts in service for safety The English and Romanian prompts now include specific JSON examples in their respective languages, ensuring the LLM understands the expected output format for each language variant. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>The job-search status page HTML wrapper was baked into a static helper method in CvMatcherController. Extracted to a new template key html.job-search.shell (*) with {{title}} and {{message}} placeholders. Added to AddTemplates seed and a new AddHtmlJobSearchShell migration for existing DBs. Controller now calls _templates.Render() for all paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>- Require captchaToken query param on initial (non-range) download requests - Range requests (HTTP resume) bypass captcha — they are continuations of an already-validated download - Add download rate limit policy: 5 requests / 1 min per IP (configured in .env) - Inject ICaptchaVerifier; action name is file_download UI change required: execute grecaptcha.execute(siteKey, {action: 'file_download'}) before triggering the download and append ?captchaToken=<token> to the URL. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>5 requests / 1 min per IP. docker-compose.yml wired with ${VAR:-default}. Staging and production .env files updated locally (gitignored). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>Show the candidate's location in the scan summary block of the results email alongside keywords and providers, for both en and ro templates. - CvSearchEmailSender.SendResultsAsync accepts location and passes it to BuildScanSummary - BuildScanSummary passes {{location}} to the template (falls back to '-' when absent) - CvSearchJobTask passes session.Location to SendResultsAsync - New migration AddLocationToScanSummaryTemplate updates both language variants of email.search-results.scan-summary to include a 'Location / Locație căutată' row Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>