@@ -9,6 +9,7 @@ defmodule SentinelCp.Services do
99 import Ecto.Query , warn: false
1010 alias SentinelCp.Repo
1111 alias SentinelCp.Services . { Service , ServiceTemplate , ProjectConfig , UpstreamGroup , UpstreamTarget , Certificate , AuthPolicy , OpenApiSpec , DiscoverySource , DiscoverySync , Middleware , ServiceMiddleware }
12+ alias SentinelCp.Secrets
1213
1314 ## Services
1415
@@ -326,6 +327,36 @@ defmodule SentinelCp.Services do
326327 |> Repo . all ( )
327328 end
328329
330+ @ doc """
331+ Lists certificates eligible for ACME auto-renewal.
332+
333+ Finds certificates with `auto_renew: true`, non-empty `acme_config`,
334+ and expiring within the configured threshold (default 30 days).
335+ """
336+ def list_acme_renewal_candidates ( days_ahead \\ 30 ) do
337+ now = DateTime . utc_now ( )
338+ threshold = DateTime . add ( now , days_ahead * 86_400 , :second )
339+
340+ from ( c in Certificate ,
341+ where: c . auto_renew == true ,
342+ where: c . status in [ "active" , "expiring_soon" ] ,
343+ where: c . not_after > ^ now ,
344+ where: c . not_after <= ^ threshold ,
345+ order_by: [ asc: c . not_after ]
346+ )
347+ |> Repo . all ( )
348+ |> Enum . filter ( & ( ( & 1 . acme_config || % { } ) != % { } ) )
349+ end
350+
351+ @ doc """
352+ Updates ACME-specific fields on a certificate (account key, renewal status).
353+ """
354+ def update_certificate_acme ( % Certificate { } = cert , attrs ) do
355+ cert
356+ |> Certificate . acme_changeset ( attrs )
357+ |> Repo . update ( )
358+ end
359+
329360 ## Service Templates
330361
331362 @ doc """
@@ -560,7 +591,7 @@ defmodule SentinelCp.Services do
560591 |> DiscoverySource . sync_changeset ( % { last_sync_status: "syncing" } )
561592 |> Repo . update ( )
562593
563- case dns_resolver ( ) . resolve_srv ( source . hostname ) do
594+ case resolve_records ( source ) do
564595 { :ok , records } ->
565596 group = get_upstream_group! ( source . upstream_group_id )
566597 current_targets = group . targets || [ ]
@@ -887,10 +918,28 @@ defmodule SentinelCp.Services do
887918 service_edges ++ target_edges
888919 end
889920
921+ defp resolve_records ( % DiscoverySource { source_type: "kubernetes" } = source ) do
922+ case Secrets . resolve_references ( source . config || % { } , source . project_id ) do
923+ { :ok , resolved_config } ->
924+ k8s_resolver ( ) . resolve_endpoints ( resolved_config )
925+
926+ { :error , reason } ->
927+ { :error , "Secret resolution failed: #{ inspect ( reason ) } " }
928+ end
929+ end
930+
931+ defp resolve_records ( % DiscoverySource { } = source ) do
932+ dns_resolver ( ) . resolve_srv ( source . hostname )
933+ end
934+
890935 defp dns_resolver do
891936 Application . get_env ( :sentinel_cp , :dns_resolver , SentinelCp.Services.DnsResolver.Inet )
892937 end
893938
939+ defp k8s_resolver do
940+ Application . get_env ( :sentinel_cp , :k8s_resolver , SentinelCp.Services.K8sResolver.HTTP )
941+ end
942+
894943 defp resolve_auth_policy_id ( % { security_refs: refs } , policy_map ) when is_list ( refs ) do
895944 Enum . find_value ( refs , fn ref -> Map . get ( policy_map , ref ) end )
896945 end
0 commit comments