mirror of
https://github.com/domfelipe/mika-agent-assist.git
synced 2026-08-07 06:56:44 +00:00
Corrigiu replica via sleepApp
X-Lovable-Edit-ID: edt-d299c2dd-2ed1-4767-ae85-143c3feababf Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com>
This commit is contained in:
commit
50a0c9042e
3 changed files with 87 additions and 24 deletions
|
|
@ -137,12 +137,19 @@ export async function deployRailwayService(opts: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suspende ou retoma um serviço Railway usando `sleepApplication`.
|
||||||
|
* Railway não aceita `numReplicas: 0` via serviceInstanceUpdate — o caminho
|
||||||
|
* suportado para pausar é `sleepApplication: true` (e `false` para acordar).
|
||||||
|
* `replicas <= 0` => sleep, `>= 1` => wake.
|
||||||
|
*/
|
||||||
export async function setRailwayReplicas(opts: {
|
export async function setRailwayReplicas(opts: {
|
||||||
token: string;
|
token: string;
|
||||||
serviceId: string;
|
serviceId: string;
|
||||||
environmentId: string;
|
environmentId: string;
|
||||||
replicas: number;
|
replicas: number;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
|
const sleep = opts.replicas <= 0;
|
||||||
const mutation = `
|
const mutation = `
|
||||||
mutation ServiceInstanceUpdate($serviceId: String!, $environmentId: String!, $input: ServiceInstanceUpdateInput!) {
|
mutation ServiceInstanceUpdate($serviceId: String!, $environmentId: String!, $input: ServiceInstanceUpdateInput!) {
|
||||||
serviceInstanceUpdate(serviceId: $serviceId, environmentId: $environmentId, input: $input)
|
serviceInstanceUpdate(serviceId: $serviceId, environmentId: $environmentId, input: $input)
|
||||||
|
|
@ -153,13 +160,49 @@ export async function setRailwayReplicas(opts: {
|
||||||
{
|
{
|
||||||
serviceId: opts.serviceId,
|
serviceId: opts.serviceId,
|
||||||
environmentId: opts.environmentId,
|
environmentId: opts.environmentId,
|
||||||
input: { numReplicas: opts.replicas },
|
input: { sleepApplication: sleep },
|
||||||
},
|
},
|
||||||
opts.token,
|
opts.token,
|
||||||
);
|
);
|
||||||
if (res.errors?.length) {
|
if (res.errors?.length) {
|
||||||
throw new Error(`setRailwayReplicas failed: ${JSON.stringify(res.errors)}`);
|
throw new Error(`setRailwayReplicas (sleepApplication=${sleep}) failed: ${JSON.stringify(res.errors)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Força redeploy para aplicar imediatamente o novo estado de sleep
|
||||||
|
try {
|
||||||
|
await deployRailwayService({
|
||||||
|
token: opts.token,
|
||||||
|
serviceId: opts.serviceId,
|
||||||
|
environmentId: opts.environmentId,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("setRailwayReplicas: redeploy after sleep change failed (non-fatal):", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Busca o environmentId do primeiro deployment de um serviço. Útil quando vps_pool_id está null. */
|
||||||
|
export async function getServiceEnvironmentId(opts: {
|
||||||
|
token: string;
|
||||||
|
serviceId: string;
|
||||||
|
}): Promise<string | null> {
|
||||||
|
const query = `
|
||||||
|
query Service($id: String!) {
|
||||||
|
service(id: $id) {
|
||||||
|
projectId
|
||||||
|
deployments(first: 1) {
|
||||||
|
edges { node { environmentId } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const res = await railwayQuery<{
|
||||||
|
service: { projectId: string; deployments: { edges: { node: { environmentId: string } }[] } };
|
||||||
|
}>(query, { id: opts.serviceId }, opts.token);
|
||||||
|
if (res.errors?.length) {
|
||||||
|
console.error("getServiceEnvironmentId errors:", JSON.stringify(res.errors));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return res.data?.service?.deployments?.edges?.[0]?.node?.environmentId ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Apaga o webhook do Telegram para que o Hermes assuma via polling. */
|
/** Apaga o webhook do Telegram para que o Hermes assuma via polling. */
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.45.4";
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.45.4";
|
||||||
import { corsHeaders } from "../_shared/cors.ts";
|
import { corsHeaders } from "../_shared/cors.ts";
|
||||||
import { setRailwayReplicas } from "../_shared/railway.ts";
|
import { setRailwayReplicas, getServiceEnvironmentId } from "../_shared/railway.ts";
|
||||||
|
|
||||||
const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!;
|
const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!;
|
||||||
const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
||||||
|
|
@ -45,7 +45,7 @@ Deno.serve(async (req) => {
|
||||||
|
|
||||||
if (!agent) return jsonResponse(404, { error: "agent_instance not found" });
|
if (!agent) return jsonResponse(404, { error: "agent_instance not found" });
|
||||||
|
|
||||||
if (!agent.railway_service_id || !agent.vps_pool_id) {
|
if (!agent.railway_service_id) {
|
||||||
return jsonResponse(409, {
|
return jsonResponse(409, {
|
||||||
error: "agent has no container — needs full provisioning instead",
|
error: "agent has no container — needs full provisioning instead",
|
||||||
});
|
});
|
||||||
|
|
@ -55,21 +55,31 @@ Deno.serve(async (req) => {
|
||||||
return jsonResponse(200, { ok: true, already_active: true });
|
return jsonResponse(200, { ok: true, already_active: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data: pool } = await supabase
|
// Resolve environmentId: prioriza vps_pool, fallback para query no Railway
|
||||||
.from("vps_pool")
|
let environmentId: string | null = null;
|
||||||
.select("railway_environment_id")
|
if (agent.vps_pool_id) {
|
||||||
.eq("id", agent.vps_pool_id)
|
const { data: pool } = await supabase
|
||||||
.maybeSingle();
|
.from("vps_pool")
|
||||||
|
.select("railway_environment_id")
|
||||||
if (!pool?.railway_environment_id) {
|
.eq("id", agent.vps_pool_id)
|
||||||
return jsonResponse(500, { error: "pool environment not configured" });
|
.maybeSingle();
|
||||||
|
environmentId = pool?.railway_environment_id ?? null;
|
||||||
|
}
|
||||||
|
if (!environmentId) {
|
||||||
|
environmentId = await getServiceEnvironmentId({
|
||||||
|
token: RAILWAY_API_TOKEN,
|
||||||
|
serviceId: agent.railway_service_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!environmentId) {
|
||||||
|
return jsonResponse(500, { error: "could not resolve railway environmentId" });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await setRailwayReplicas({
|
await setRailwayReplicas({
|
||||||
token: RAILWAY_API_TOKEN,
|
token: RAILWAY_API_TOKEN,
|
||||||
serviceId: agent.railway_service_id,
|
serviceId: agent.railway_service_id,
|
||||||
environmentId: pool.railway_environment_id,
|
environmentId,
|
||||||
replicas: 1,
|
replicas: 1,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.45.4";
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.45.4";
|
||||||
import { corsHeaders } from "../_shared/cors.ts";
|
import { corsHeaders } from "../_shared/cors.ts";
|
||||||
import { setRailwayReplicas } from "../_shared/railway.ts";
|
import { setRailwayReplicas, getServiceEnvironmentId } from "../_shared/railway.ts";
|
||||||
|
|
||||||
const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!;
|
const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!;
|
||||||
const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
||||||
|
|
@ -47,7 +47,7 @@ Deno.serve(async (req) => {
|
||||||
if (agent.status === "suspended") {
|
if (agent.status === "suspended") {
|
||||||
return jsonResponse(200, { ok: true, already_suspended: true });
|
return jsonResponse(200, { ok: true, already_suspended: true });
|
||||||
}
|
}
|
||||||
if (!agent.railway_service_id || !agent.vps_pool_id) {
|
if (!agent.railway_service_id) {
|
||||||
// Sem container provisionado ainda — só marca o status
|
// Sem container provisionado ainda — só marca o status
|
||||||
await supabase
|
await supabase
|
||||||
.from("agent_instances")
|
.from("agent_instances")
|
||||||
|
|
@ -56,21 +56,31 @@ Deno.serve(async (req) => {
|
||||||
return jsonResponse(200, { ok: true, no_container: true });
|
return jsonResponse(200, { ok: true, no_container: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data: pool } = await supabase
|
// Resolve o environmentId: prioriza vps_pool, fallback para query no Railway
|
||||||
.from("vps_pool")
|
let environmentId: string | null = null;
|
||||||
.select("railway_environment_id")
|
if (agent.vps_pool_id) {
|
||||||
.eq("id", agent.vps_pool_id)
|
const { data: pool } = await supabase
|
||||||
.maybeSingle();
|
.from("vps_pool")
|
||||||
|
.select("railway_environment_id")
|
||||||
if (!pool?.railway_environment_id) {
|
.eq("id", agent.vps_pool_id)
|
||||||
return jsonResponse(500, { error: "pool environment not configured" });
|
.maybeSingle();
|
||||||
|
environmentId = pool?.railway_environment_id ?? null;
|
||||||
|
}
|
||||||
|
if (!environmentId) {
|
||||||
|
environmentId = await getServiceEnvironmentId({
|
||||||
|
token: RAILWAY_API_TOKEN,
|
||||||
|
serviceId: agent.railway_service_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!environmentId) {
|
||||||
|
return jsonResponse(500, { error: "could not resolve railway environmentId" });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await setRailwayReplicas({
|
await setRailwayReplicas({
|
||||||
token: RAILWAY_API_TOKEN,
|
token: RAILWAY_API_TOKEN,
|
||||||
serviceId: agent.railway_service_id,
|
serviceId: agent.railway_service_id,
|
||||||
environmentId: pool.railway_environment_id,
|
environmentId,
|
||||||
replicas: 0,
|
replicas: 0,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue