From 64914c8b9a18284b84c057df111863a43228dd66 Mon Sep 17 00:00:00 2001 From: SonPhung Date: Tue, 21 Jul 2026 23:54:13 +0700 Subject: [PATCH] feat: initialize loyalty agent service structure, security configuration, and campaign MCP service orchestration --- .../loyalty/agent/config/AgentConfig.java | 30 ------ .../loyalty/agent/config/SecurityConfig.java | 22 +--- .../src/main/resources/application.yml | 15 --- .../loyalty/mcp/service/CampaignService.java | 18 +++- start-all.ps1 | 101 ++++++++++++++++++ 5 files changed, 119 insertions(+), 67 deletions(-) create mode 100644 start-all.ps1 diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/AgentConfig.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/AgentConfig.java index 8aa83e0..3e3b76f 100644 --- a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/AgentConfig.java +++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/AgentConfig.java @@ -2,7 +2,6 @@ package dev.sonpx.loyalty.agent.config; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -18,39 +17,10 @@ public class AgentConfig { 2. When a tool result contains an `agent_instruction`, you MUST strictly follow its formatting rules when presenting the relevant data to the user. Do not output raw JSON data unless explicitly asked. """; - @Value("${loyalty.core.base-url}") - private String coreBaseUrl; - @Bean public ChatClient loyaltyChatClient(ChatModel chatModel) { return ChatClient.builder(chatModel) .defaultSystem(AGENT_SYSTEM_PROMPT) .build(); } - -// @Bean -// public RestClient coreRestClient(OAuth2AuthorizedClientManager authorizedClientManager) { -// return RestClient.builder() -// .baseUrl(coreBaseUrl) -// .requestInterceptor((request, body, execution) -> { -// org.springframework.security.core.Authentication principal = -// new org.springframework.security.authentication.AnonymousAuthenticationToken( -// "key", "loyalty-agent-service", -// org.springframework.security.core.authority.AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); -// OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest -// .withClientRegistrationId("keycloak") -// .principal(principal) -// .build(); -// try { -// OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest); -// if (authorizedClient != null && authorizedClient.getAccessToken() != null) { -// request.getHeaders().setBearerAuth(authorizedClient.getAccessToken().getTokenValue()); -// } -// } catch (Exception e) { -// e.printStackTrace(); -// } -// return execution.execute(request, body); -// }) -// .build(); -// } } diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/SecurityConfig.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/SecurityConfig.java index 54f34e2..2be2be9 100644 --- a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/SecurityConfig.java +++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/SecurityConfig.java @@ -12,33 +12,13 @@ import org.springframework.security.web.SecurityFilterChain; public class SecurityConfig { @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain filterChain(HttpSecurity http) { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(authz -> authz .requestMatchers("/api/v1/agent/**", "/api/v1/conversations/**", "/ws/**", "/error").permitAll() .anyRequest().authenticated() ); -// .oauth2Login(Customizer.withDefaults()) -// .oauth2Client(Customizer.withDefaults()); return http.build(); } - -// @Bean -// public OAuth2AuthorizedClientManager authorizedClientManager( -// ClientRegistrationRepository clientRegistrationRepository, -// OAuth2AuthorizedClientService authorizedClientService) { -// -// OAuth2AuthorizedClientProvider authorizedClientProvider = -// OAuth2AuthorizedClientProviderBuilder.builder() -// .clientCredentials() -// .build(); -// -// AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager = -// new AuthorizedClientServiceOAuth2AuthorizedClientManager( -// clientRegistrationRepository, authorizedClientService); -// authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); -// -// return authorizedClientManager; -// } } diff --git a/loyalty-agent/src/main/resources/application.yml b/loyalty-agent/src/main/resources/application.yml index 2d90c9c..a3529c3 100644 --- a/loyalty-agent/src/main/resources/application.yml +++ b/loyalty-agent/src/main/resources/application.yml @@ -23,21 +23,6 @@ spring: connections: loyalty: url: ${MCP_SERVER_URL:http://localhost:9331} -# security: -# oauth2: -# client: -# registration: -# keycloak: -# client-id: ols-cli -# client-secret: ${KEYCLOAK_CLIENT_SECRET:1jV8NSAeybIqmUK0AWhI8hgdo1q5itj7} -# authorization-grant-type: client_credentials -# provider: -# keycloak: -# token-uri: ${KEYCLOAK_TOKEN_URI:http://192.168.99.235/auth/realms/ols-cn-sit/protocol/openid-connect/token} - -#loyalty: -# core: -# base-url: ${LOYALTY_CORE_BASE_URL:http://192.168.99.242:8081} logging: pattern: diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/service/CampaignService.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/service/CampaignService.java index 8be2daf..7ca8e61 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/service/CampaignService.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/service/CampaignService.java @@ -23,7 +23,23 @@ public class CampaignService { public Result> getAll(CampaignCriteria criteria) { List campaigns = campaignClient.getAll(criteria, Pageable.ofSize(5)); - return Result.of(campaigns, "Format these campaigns as a markdown table with columns ID, Name, Start Date, End Date, Status."); + return Result.of(campaigns, """ + Khi trả về danh sách chiến dịch, BẮT BUỘC KHÔNG dùng markdown table. Hãy định dạng theo cấu trúc sau: + + 1. **[Tên Chiến Dịch]** (ID: `[Mã ID]`) + - 👤 Owner: [Tên Owner] (Nếu trống, ghi là: Không có) + - 📝 Mô tả: [Mô tả] (Nếu trống, ghi là: Không có. Nếu dài hơn 100 ký tự, thêm "...") + Ví dụ đầu ra ĐÚNG: + 1. **Happy Birthday!** (ID: `LACO`) + - 👤 Owner: Không có + - 📝 Mô tả: Không có + 2. **SummerSale GOTIT Campaign** (ID: `YZFG`) + - 👤 Owner: Ngan Ha + - 📝 Mô tả: Không có + + Luôn kết thúc bằng một câu hỏi gợi mở để hướng dẫn người dùng bước tiếp theo. + + """); } public Result count(CampaignCriteria criteria) { diff --git a/start-all.ps1 b/start-all.ps1 new file mode 100644 index 0000000..9a647bc --- /dev/null +++ b/start-all.ps1 @@ -0,0 +1,101 @@ +$ErrorActionPreference = "Stop" +$ProjectRoot = $PSScriptRoot +$LogDir = Join-Path $ProjectRoot "log" + +if (-not (Test-Path $LogDir)) { + New-Item -ItemType Directory -Force -Path $LogDir | Out-Null +} + +if (Test-Path (Join-Path $ProjectRoot ".env")) { + Write-Host "Loading environment variables from .env" + Get-Content (Join-Path $ProjectRoot ".env") | Where-Object { $_ -match "^[^#]" -and $_ -match "=" } | ForEach-Object { + $name, $value = $_.Split("=", 2) + [System.Environment]::SetEnvironmentVariable($name, $value) + } +} + +Write-Host "==========================================" +Write-Host "Cleaning up existing processes..." +Write-Host "==========================================" +foreach ($port in 9331, 9332, 9333) { + $netstat = netstat -ano | Select-String "LISTENING" | Select-String ":$port" + if ($netstat) { + foreach ($line in $netstat) { + $parts = $line -split '\s+' + $pidToKill = $parts[-1] + if ($pidToKill -and $pidToKill -ne "0") { + Write-Host "Killing process on port $port (PID: $pidToKill)..." + Stop-Process -Id $pidToKill -Force -ErrorAction SilentlyContinue + } + } + } +} + +$ProcessList = @() + +try { + Write-Host "==========================================" + Write-Host "Starting Loyalty Agent Services" + Write-Host "==========================================" + + # 1. Start MCP Server + Write-Host "Starting loyalty-mcp-server..." + $mcpLog = Join-Path $LogDir "loyalty-mcp-server.log" + $mcpProcess = Start-Process -FilePath "cmd.exe" -ArgumentList "/c ..\mvnw.cmd spring-boot:run -Dmaven.test.skip=true > `"$mcpLog`" 2>&1" -WorkingDirectory (Join-Path $ProjectRoot "loyalty-mcp-server") -PassThru -WindowStyle Hidden + $ProcessList += $mcpProcess + Write-Host "loyalty-mcp-server started with PID: $($mcpProcess.Id)" + + Write-Host "Waiting for loyalty-mcp-server to initialize..." + while ($true) { + $netstat = netstat -ano | Select-String "LISTENING" | Select-String ":9331" + if ($netstat) { break } + Start-Sleep -Seconds 2 + } + Write-Host "loyalty-mcp-server is up!" + + # 2. Start Agent Service + Write-Host "Starting loyalty-agent..." + $agentLog = Join-Path $LogDir "loyalty-agent.log" + $agentProcess = Start-Process -FilePath "cmd.exe" -ArgumentList "/c ..\mvnw.cmd spring-boot:run -Dmaven.test.skip=true > `"$agentLog`" 2>&1" -WorkingDirectory (Join-Path $ProjectRoot "loyalty-agent") -PassThru -WindowStyle Hidden + $ProcessList += $agentProcess + Write-Host "loyalty-agent started with PID: $($agentProcess.Id)" + + Write-Host "Waiting for loyalty-agent to initialize..." + while ($true) { + $netstat = netstat -ano | Select-String "LISTENING" | Select-String ":9332" + if ($netstat) { break } + Start-Sleep -Seconds 2 + } + Write-Host "loyalty-agent is up!" + + # 3. Start Frontend UI + Write-Host "Starting Frontend UI..." + $uiLog = Join-Path $LogDir "frontend.log" + $uiProcess = Start-Process -FilePath "cmd.exe" -ArgumentList "/c pnpm dev > `"$uiLog`" 2>&1" -WorkingDirectory $ProjectRoot -PassThru -WindowStyle Hidden + $ProcessList += $uiProcess + Write-Host "Frontend UI started with PID: $($uiProcess.Id)" + + Write-Host "==========================================" + Write-Host "All services started successfully!" + Write-Host "Log files are stored in: $LogDir" + Write-Host "- $mcpLog" + Write-Host "- $agentLog" + Write-Host "- $uiLog" + Write-Host "==========================================" + Write-Host "Press Ctrl+C to stop all services..." + + # Wait indefinitely + while ($true) { + Start-Sleep -Seconds 1 + } +} +finally { + Write-Host "`nStopping services..." + foreach ($p in $ProcessList) { + if (-not $p.HasExited) { + taskkill /PID $p.Id /T /F 2>&1 | Out-Null + Write-Host "Stopped process tree for PID $($p.Id)" + } + } + Write-Host "All services stopped." +}