changemaker.lite/api/dist/services/listmonk.client.js

182 lines
6.4 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.listmonkClient = void 0;
const env_1 = require("../config/env");
const logger_1 = require("../utils/logger");
// --- Client ---
class ListmonkClient {
get baseUrl() {
return env_1.env.LISTMONK_URL;
}
get authHeader() {
return 'Basic ' + Buffer.from(`${env_1.env.LISTMONK_ADMIN_USER}:${env_1.env.LISTMONK_ADMIN_PASSWORD}`).toString('base64');
}
get enabled() {
return env_1.env.LISTMONK_SYNC_ENABLED === 'true';
}
assertEnabled() {
if (!this.enabled) {
throw new Error('Listmonk sync is disabled. Set LISTMONK_SYNC_ENABLED=true to enable.');
}
}
async request(method, path, body) {
const url = `${this.baseUrl}${path}`;
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const res = await fetch(url, {
method,
headers: {
'Authorization': this.authHeader,
'Content-Type': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(`Listmonk API ${method} ${path} returned ${res.status}: ${text}`);
}
return await res.json();
}
finally {
clearTimeout(timeout);
}
}
async checkHealth() {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const res = await fetch(`${this.baseUrl}/api/health`, {
headers: { 'Authorization': this.authHeader },
signal: controller.signal,
});
return res.ok;
}
finally {
clearTimeout(timeout);
}
}
catch (err) {
logger_1.logger.warn('Listmonk health check failed:', err instanceof Error ? err.message : err);
return false;
}
}
async getLists() {
this.assertEnabled();
try {
const res = await this.request('GET', '/api/lists?per_page=all');
return res.data.results || [];
}
catch (err) {
logger_1.logger.warn('Failed to fetch Listmonk lists:', err instanceof Error ? err.message : err);
throw err;
}
}
async createList(name, type, tags) {
this.assertEnabled();
try {
const res = await this.request('POST', '/api/lists', {
name,
type,
optin: 'single',
tags,
});
return res.data;
}
catch (err) {
logger_1.logger.warn(`Failed to create Listmonk list "${name}":`, err instanceof Error ? err.message : err);
throw err;
}
}
async findSubscriberByEmail(email) {
this.assertEnabled();
try {
const safeEmail = email.replace(/'/g, "''");
const query = encodeURIComponent(`subscribers.email='${safeEmail}'`);
const res = await this.request('GET', `/api/subscribers?query=${query}&per_page=1`);
const results = res.data.results;
return results && results.length > 0 ? results[0] : null;
}
catch (err) {
logger_1.logger.warn(`Failed to find subscriber ${email}:`, err instanceof Error ? err.message : err);
return null;
}
}
async createSubscriber(email, name, listIds, attribs) {
this.assertEnabled();
const res = await this.request('POST', '/api/subscribers', {
email,
name,
status: 'enabled',
lists: listIds,
attribs,
});
return res.data;
}
async updateSubscriber(id, data) {
this.assertEnabled();
const res = await this.request('PUT', `/api/subscribers/${id}`, data);
return res.data;
}
async updateList(id, data) {
this.assertEnabled();
const res = await this.request('PUT', `/api/lists/${id}`, data);
return res.data;
}
async deleteList(id) {
this.assertEnabled();
await this.request('DELETE', `/api/lists/${id}`);
}
async removeSubscriberFromLists(subscriberId, listIdsToRemove, currentEmail, currentListIds) {
this.assertEnabled();
const filteredIds = currentListIds.filter(id => !listIdsToRemove.includes(id));
return this.updateSubscriber(subscriberId, {
email: currentEmail,
lists: filteredIds,
});
}
/**
* Get campaigns with stats (for dashboard)
*/
async getCampaigns() {
try {
const res = await this.request('GET', '/api/campaigns?per_page=all&order_by=updated_at&order=desc');
return (res.data.results || []).map(c => ({
id: c.id,
name: c.name,
status: c.status,
sent: c.stats?.sent || 0,
views: c.stats?.views || 0,
clicks: c.stats?.clicks || 0,
started_at: c.started_at,
updated_at: c.updated_at,
}));
}
catch (err) {
logger_1.logger.warn('Failed to fetch Listmonk campaigns:', err instanceof Error ? err.message : err);
return [];
}
}
async upsertSubscriber(email, name, listIds, attribs) {
this.assertEnabled();
const existing = await this.findSubscriberByEmail(email);
if (existing) {
// Merge lists: combine existing + new, deduplicate
const existingListIds = existing.lists.map(l => l.id);
const mergedListIds = [...new Set([...existingListIds, ...listIds])];
// Merge attribs
const mergedAttribs = { ...existing.attribs, ...attribs };
return this.updateSubscriber(existing.id, {
email,
name: name || existing.name,
lists: mergedListIds,
attribs: mergedAttribs,
});
}
return this.createSubscriber(email, name, listIds, attribs);
}
}
exports.listmonkClient = new ListmonkClient();
//# sourceMappingURL=listmonk.client.js.map