import { t } from "../i18n.js";
document.addEventListener("alpine:init", () => {
Alpine.data("serviceExport", () => ({
get store() {
return Alpine.store("app");
},
loading: false,
error: "",
plan: null,
picked: {}, includeValues: true,
filter: "",
copyMessage: "",
init() {
this.$watch("$store.app.exportWin", (exportWindow) => (exportWindow ? this.load(exportWindow.serviceId) : this.reset()));
},
reset() {
this.loading = false;
this.error = "";
this.plan = null;
this.picked = {};
this.includeValues = true;
this.filter = "";
this.copyMessage = "";
},
async load(serviceId) {
this.reset();
this.loading = true;
const serviceExportPlanResponse = await this.store.api.rpcChecked({ ServiceExportPlan: { service_id: serviceId } });
this.loading = false;
if (!serviceExportPlanResponse.ok || !serviceExportPlanResponse.value?.ServiceExportPlan) {
this.error = serviceExportPlanResponse.ok ? t("resposta inesperada do daemon") : serviceExportPlanResponse.error;
return;
}
this.plan = serviceExportPlanResponse.value.ServiceExportPlan;
this.pickSuggested();
},
get projectVars() {
const filterText = this.filter.trim().toLowerCase();
const all = this.plan?.project_env || [];
return filterText ? all.filter((item) => item.key.toLowerCase().includes(filterText)) : all;
},
get pickedKeys() {
return (this.plan?.project_env || []).filter((envVar) => this.picked[envVar.key]).map((envVar) => envVar.key);
},
pickSuggested() {
const pickedMap = {};
for (const envVar of this.plan?.project_env || []) pickedMap[envVar.key] = !!envVar.suggested;
this.picked = pickedMap;
},
pickAll() {
const pickedMap = {};
for (const envVar of this.plan?.project_env || []) pickedMap[envVar.key] = true;
this.picked = pickedMap;
},
pickNone() {
this.picked = {};
},
async generate() {
this.error = "";
const serviceExportResponse = await this.store.api.rpcChecked({
ServiceExport: {
service_id: this.store.exportWin.serviceId,
include_values: this.includeValues,
project_env_keys: this.pickedKeys,
},
});
if (!serviceExportResponse.ok || !serviceExportResponse.value?.ServiceBundleYaml) {
this.error = serviceExportResponse.ok ? t("resposta inesperada do daemon") : serviceExportResponse.error;
return null;
}
return serviceExportResponse.value.ServiceBundleYaml;
},
async download() {
const bundle = await this.generate();
if (!bundle) return;
const url = URL.createObjectURL(new Blob([bundle.yaml], { type: "text/yaml" }));
const link = document.createElement("a");
link.href = url;
link.download = bundle.filename;
document.body.appendChild(link);
link.click();
link.remove();
setTimeout(() => URL.revokeObjectURL(url), 1000);
this.store.toastOk(t`arquivo gerado: ${bundle.filename}`);
},
async copy() {
const bundle = await this.generate();
if (!bundle) return;
try {
await navigator.clipboard.writeText(bundle.yaml);
this.store.toastOk(t("pacote copiado como texto"));
} catch {
this.error = t("o navegador não deixou copiar; use \"Baixar .yml\"");
}
},
close() {
this.store.closeExportWin();
},
}));
Alpine.data("serviceImport", () => ({
get store() {
return Alpine.store("app");
},
text: "",
fileName: "",
report: null,
analyzing: false,
creating: false,
error: "",
seq: 0,
name: "",
dropDomains: false,
gitProviderId: "",
deploy: false,
vars: {}, projectVars: {}, secrets: {}, projectEnv: {}, gitProviders: [],
async pickFile(changeEvent) {
const file = changeEvent.target.files && changeEvent.target.files[0];
if (!file) return;
this.fileName = file.name;
this.text = await file.text();
await this.analyze(true);
},
clear() {
this.text = "";
this.fileName = "";
this.report = null;
this.error = "";
this.name = "";
this.dropDomains = false;
this.gitProviderId = "";
this.deploy = false;
this.vars = {};
this.projectVars = {};
this.secrets = {};
this.projectEnv = {};
},
nonEmpty(map) {
const out = {};
for (const [key, value] of Object.entries(map)) if (value !== "") out[key] = value;
return out;
},
buildReq(dryRun) {
return {
yaml: this.text,
project_id: this.store.selectedProjectId,
name: this.name.trim() || null,
drop_domains: this.dropDomains,
git_provider_id: this.gitProviderId || null,
vars: this.nonEmpty(this.vars),
project_vars: this.nonEmpty(this.projectVars),
secrets: this.nonEmpty(this.secrets),
project_env: this.projectEnv,
deploy: this.deploy,
dry_run: dryRun,
};
},
async analyze(first = false) {
if (!this.text.trim()) {
this.error = t("escolha um arquivo ou cole o texto do pacote");
return;
}
if (first) this.name = "";
this.error = "";
const mine = ++this.seq;
this.analyzing = true;
const serviceImportResponse = await this.store.api.rpcChecked({ ServiceImport: this.buildReq(true) });
if (mine !== this.seq) return;
this.analyzing = false;
if (!serviceImportResponse.ok || !serviceImportResponse.value?.ServiceImportReport) {
this.report = null;
this.error = serviceImportResponse.ok ? t("resposta inesperada do daemon") : serviceImportResponse.error;
return;
}
this.report = serviceImportResponse.value.ServiceImportReport;
if (first || !this.name) this.name = this.report.service_name;
for (const statusEntry of this.report.project_env) {
if (!this.projectEnv[statusEntry.key]) this.projectEnv[statusEntry.key] = statusEntry.choice;
}
if (this.report.missing_git_provider && this.gitProviders.length === 0) {
const gitProviderListResponse = await this.store.api.rpc("GitProviderList");
this.gitProviders = (gitProviderListResponse.ok && gitProviderListResponse.value?.GitProviders) || [];
}
},
chooseEnv(key, choice) {
if (choice === "Overwrite" && !confirm(t`Sobrescrever ${key} no projeto? Os outros serviços do projeto também passam a ver o valor novo.`)) {
this.projectEnv[key] = "Keep";
return;
}
this.projectEnv[key] = choice;
this.analyze();
},
get hasMissing() {
const report = this.report;
return !!report && (report.missing_service_vars.length > 0 || report.missing_project_vars.length > 0);
},
get canCreate() {
return !!this.report && !this.report.name_conflict && !this.hasMissing && !this.analyzing;
},
stateLabel(state) {
return { New: "nova", Same: "igual", Conflict: t("em conflito") }[state] || state;
},
choiceLabel(statusEntry, choice) {
if (choice === "Keep") return statusEntry.state === "New" ? t("criar no projeto") : t("manter a do projeto");
return { Overwrite: t("sobrescrever a do projeto"), ServiceOnly: t("só neste serviço"), Ignore: t("não trazer") }[choice];
},
choicesFor(statusEntry) {
return statusEntry.state === "Conflict" ? ["Keep", t("Overwrite"), "ServiceOnly", "Ignore"] : ["Keep", "ServiceOnly", "Ignore"];
},
async create() {
this.error = "";
this.creating = true;
const serviceImportResponse = await this.store.api.rpcChecked({ ServiceImport: this.buildReq(false) });
this.creating = false;
if (!serviceImportResponse.ok || !serviceImportResponse.value?.ServiceImportReport) {
this.error = serviceImportResponse.ok ? t("resposta inesperada do daemon") : serviceImportResponse.error;
return;
}
const done = serviceImportResponse.value.ServiceImportReport;
this.store.toastOk(t`serviço '${done.service_name}' criado`);
for (const warning of done.warnings.filter((warning) => warning.code === "deploy_not_started")) this.store.toastWarn(warning.message);
await this.store.refreshNow();
this.store.closeNewService();
if (done.service_id) this.store.openService(done.service_id);
},
}));
});