@geekapps/post-fastify 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +68 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +2 -2
- package/src/plugin.ts +77 -12
package/dist/index.d.ts
CHANGED
|
@@ -518,7 +518,7 @@ declare class PostClient {
|
|
|
518
518
|
readonly history: HistoryResource;
|
|
519
519
|
readonly statistics: StatisticsResource;
|
|
520
520
|
readonly managedNumbers: ManagedNumbersResource;
|
|
521
|
-
constructor(
|
|
521
|
+
constructor(optionsOrHttp: HttpClientOptions | HttpClient);
|
|
522
522
|
}
|
|
523
523
|
|
|
524
524
|
declare class PostApiError extends Error {
|
package/dist/index.js
CHANGED
|
@@ -345,8 +345,8 @@ var WhatsappTemplatesResource = class {
|
|
|
345
345
|
|
|
346
346
|
// src/client.ts
|
|
347
347
|
var PostClient = class {
|
|
348
|
-
constructor(
|
|
349
|
-
this.http = new HttpClient(
|
|
348
|
+
constructor(optionsOrHttp) {
|
|
349
|
+
this.http = optionsOrHttp instanceof HttpClient ? optionsOrHttp : new HttpClient(optionsOrHttp);
|
|
350
350
|
this.messages = new MessagesResource(this.http);
|
|
351
351
|
this.templates = new TemplatesResource(this.http);
|
|
352
352
|
this.whatsappTemplates = new WhatsappTemplatesResource(this.http);
|
|
@@ -363,6 +363,15 @@ var PostClient = class {
|
|
|
363
363
|
|
|
364
364
|
// src/plugin.ts
|
|
365
365
|
import fp from "fastify-plugin";
|
|
366
|
+
function log(logger, msg, data) {
|
|
367
|
+
if (data) {
|
|
368
|
+
process.stdout.write(`[geekapps-post] ${msg} ${JSON.stringify(data, null, 2)}
|
|
369
|
+
`);
|
|
370
|
+
} else {
|
|
371
|
+
process.stdout.write(`[geekapps-post] ${msg}
|
|
372
|
+
`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
366
375
|
function decodeJwtPayload(token) {
|
|
367
376
|
try {
|
|
368
377
|
const [, payload] = token.split(".");
|
|
@@ -374,17 +383,23 @@ function decodeJwtPayload(token) {
|
|
|
374
383
|
}
|
|
375
384
|
}
|
|
376
385
|
var TokenProvider = class {
|
|
377
|
-
constructor(options,
|
|
386
|
+
constructor(options, logger) {
|
|
378
387
|
this.cached = null;
|
|
379
388
|
this.inflight = null;
|
|
380
389
|
this.issuerUrl = options.serviceAccount.issuerUrl.replace(/\/$/, "");
|
|
381
390
|
this.clientId = options.serviceAccount.clientId;
|
|
382
391
|
this.clientSecret = options.serviceAccount.clientSecret;
|
|
383
|
-
this.
|
|
392
|
+
this.logger = logger;
|
|
384
393
|
}
|
|
385
394
|
async ensureFresh() {
|
|
386
395
|
const now = Date.now();
|
|
387
|
-
if (this.cached && this.cached.expiresAt - now > 3e4)
|
|
396
|
+
if (this.cached && this.cached.expiresAt - now > 3e4) {
|
|
397
|
+
log(this.logger, "service account token cache hit", {
|
|
398
|
+
expiresAt: new Date(this.cached.expiresAt).toISOString()
|
|
399
|
+
});
|
|
400
|
+
return this.cached;
|
|
401
|
+
}
|
|
402
|
+
log(this.logger, "service account token cache miss \u2014 fetching new token");
|
|
388
403
|
if (this.inflight) return this.inflight;
|
|
389
404
|
this.inflight = this.fetchToken().finally(() => {
|
|
390
405
|
this.inflight = null;
|
|
@@ -398,7 +413,10 @@ var TokenProvider = class {
|
|
|
398
413
|
return this.ensureFresh().then((c) => c.organizationId);
|
|
399
414
|
}
|
|
400
415
|
async fetchToken() {
|
|
401
|
-
this.
|
|
416
|
+
log(this.logger, "fetching service account token", {
|
|
417
|
+
url: `${this.issuerUrl}/auth/token`,
|
|
418
|
+
clientId: this.clientId
|
|
419
|
+
});
|
|
402
420
|
const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString("base64");
|
|
403
421
|
const res = await fetch(`${this.issuerUrl}/auth/token`, {
|
|
404
422
|
method: "POST",
|
|
@@ -410,7 +428,9 @@ var TokenProvider = class {
|
|
|
410
428
|
});
|
|
411
429
|
if (!res.ok) {
|
|
412
430
|
const err = await res.json().catch(() => ({}));
|
|
413
|
-
|
|
431
|
+
const msg = `auth failed (${res.status}): ${err.error ?? "unknown error"}`;
|
|
432
|
+
log(this.logger, msg);
|
|
433
|
+
throw new Error(`[geekapps-post] ${msg}`);
|
|
414
434
|
}
|
|
415
435
|
const data = await res.json();
|
|
416
436
|
const claims = decodeJwtPayload(data.access_token);
|
|
@@ -420,19 +440,54 @@ var TokenProvider = class {
|
|
|
420
440
|
expiresAt: Date.now() + (data.expires_in ?? 420) * 1e3,
|
|
421
441
|
organizationId
|
|
422
442
|
};
|
|
423
|
-
|
|
443
|
+
log(this.logger, "service account token acquired", {
|
|
444
|
+
claims: { organizationId },
|
|
445
|
+
expiresIn: `${data.expires_in ?? 420}s`
|
|
446
|
+
});
|
|
424
447
|
return this.cached;
|
|
425
448
|
}
|
|
426
449
|
};
|
|
450
|
+
var LoggingHttpClient = class extends HttpClient {
|
|
451
|
+
constructor(options, logger) {
|
|
452
|
+
super(options);
|
|
453
|
+
this.logger = logger;
|
|
454
|
+
this.baseUrlLog = options.baseUrl.replace(/\/$/, "");
|
|
455
|
+
}
|
|
456
|
+
async request(method, path, body) {
|
|
457
|
+
const url = `${this.baseUrlLog}/v1${path}`;
|
|
458
|
+
log(this.logger, "api request", { method, url });
|
|
459
|
+
if (body !== void 0) {
|
|
460
|
+
log(this.logger, "api request body", { body });
|
|
461
|
+
}
|
|
462
|
+
try {
|
|
463
|
+
const result = await super.request(method, path, body);
|
|
464
|
+
log(this.logger, "api response", { method, url, status: 200 });
|
|
465
|
+
return result;
|
|
466
|
+
} catch (err) {
|
|
467
|
+
const status = err.status;
|
|
468
|
+
log(this.logger, "api response error", { method, url, status });
|
|
469
|
+
throw err;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
};
|
|
427
473
|
var postPlugin = async (fastify, options) => {
|
|
428
|
-
const
|
|
474
|
+
const logger = options.logLevel ? fastify.log.child({ level: options.logLevel }) : fastify.log;
|
|
429
475
|
const baseUrl = options.baseUrl ?? "https://post-api.geekapps.com.br";
|
|
430
|
-
const provider = new TokenProvider(options,
|
|
431
|
-
|
|
476
|
+
const provider = new TokenProvider(options, logger);
|
|
477
|
+
log(logger, "geekapps post initialized", {
|
|
478
|
+
issuerUrl: options.serviceAccount.issuerUrl,
|
|
432
479
|
baseUrl,
|
|
433
|
-
|
|
434
|
-
organizationId: () => provider.getOrganizationId()
|
|
480
|
+
serviceAccount: true
|
|
435
481
|
});
|
|
482
|
+
const http = new LoggingHttpClient(
|
|
483
|
+
{
|
|
484
|
+
baseUrl,
|
|
485
|
+
token: () => provider.getToken(),
|
|
486
|
+
organizationId: () => provider.getOrganizationId()
|
|
487
|
+
},
|
|
488
|
+
logger
|
|
489
|
+
);
|
|
490
|
+
const client = new PostClient(http);
|
|
436
491
|
fastify.decorate("postClient", client);
|
|
437
492
|
fastify.addHook("onRequest", async (request) => {
|
|
438
493
|
request.postClient = client;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/domains.ts","../src/resources/history.ts","../src/resources/managed-numbers.ts","../src/resources/messages.ts","../src/resources/plans.ts","../src/resources/smtp.ts","../src/resources/statistics.ts","../src/resources/templates.ts","../src/resources/webhooks.ts","../src/resources/whatsapp-numbers.ts","../src/resources/whatsapp-templates.ts","../src/client.ts","../src/plugin.ts"],"sourcesContent":["export class PostApiError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n public readonly detail?: string,\n ) {\n super(detail ? `${message}: ${detail}` : message);\n this.name = \"PostApiError\";\n }\n\n get isNotFound() {\n return this.status === 404;\n }\n\n get isUnauthorized() {\n return this.status === 401;\n }\n\n get isForbidden() {\n return this.status === 403;\n }\n\n get isRateLimited() {\n return this.status === 429;\n }\n\n get isValidationError() {\n return this.status === 400;\n }\n}\n","import { PostApiError } from \"./errors.js\";\r\n\r\nexport type TokenResolver = string | (() => string | Promise<string>);\r\n\r\nexport interface HttpClientOptions {\r\n baseUrl: string;\r\n token: TokenResolver;\r\n organizationId?: string | (() => string | Promise<string>);\r\n}\r\n\r\nexport class HttpClient {\r\n private readonly baseUrl: string;\r\n private readonly token: TokenResolver;\r\n private readonly organizationId?: string | (() => string | Promise<string>);\r\n\r\n constructor(options: HttpClientOptions) {\r\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\r\n this.token = options.token;\r\n this.organizationId = options.organizationId;\r\n }\r\n\r\n private async resolveToken(): Promise<string> {\r\n return typeof this.token === \"function\" ? await this.token() : this.token;\r\n }\r\n\r\n private async resolveOrgId(): Promise<string | undefined> {\r\n if (!this.organizationId) return undefined;\r\n return typeof this.organizationId === \"function\"\r\n ? await this.organizationId()\r\n : this.organizationId;\r\n }\r\n\r\n async request<T = unknown>(\r\n method: string,\r\n path: string,\r\n body?: unknown,\r\n ): Promise<T> {\r\n const token = await this.resolveToken();\r\n const orgId = await this.resolveOrgId();\r\n\r\n const res = await fetch(`${this.baseUrl}/v1${path}`, {\r\n method,\r\n headers: {\r\n Authorization: `Bearer ${token}`,\r\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\r\n ...(orgId ? { \"X-Organization-Id\": orgId } : {}),\r\n },\r\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n cache: \"no-store\" as any,\r\n });\r\n\r\n if (!res.ok) {\r\n const err = await res.json().catch(() => ({})) as {\r\n error?: string;\r\n detail?: string;\r\n };\r\n throw new PostApiError(\r\n res.status,\r\n err.error ?? `HTTP ${res.status}`,\r\n err.detail,\r\n );\r\n }\r\n\r\n if (res.status === 204) return undefined as T;\r\n return res.json() as Promise<T>;\r\n }\r\n\r\n get<T>(path: string) {\r\n return this.request<T>(\"GET\", path);\r\n }\r\n\r\n post<T>(path: string, body?: unknown) {\r\n return this.request<T>(\"POST\", path, body);\r\n }\r\n\r\n put<T>(path: string, body?: unknown) {\r\n return this.request<T>(\"PUT\", path, body);\r\n }\r\n\r\n patch<T>(path: string, body?: unknown) {\r\n return this.request<T>(\"PATCH\", path, body);\r\n }\r\n\r\n delete<T = void>(path: string) {\r\n return this.request<T>(\"DELETE\", path);\r\n }\r\n}\r\n","import type { HttpClient } from \"../http.js\";\nimport type { CreateDomainInput, CreateDomainResult, OrgDomain } from \"../types.js\";\n\nexport class DomainsResource {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<OrgDomain[]> {\n return this.http.get<OrgDomain[]>(\"/domains\");\n }\n\n get(id: string): Promise<OrgDomain> {\n return this.http.get<OrgDomain>(`/domains/${id}`);\n }\n\n create(input: CreateDomainInput): Promise<CreateDomainResult> {\n return this.http.post<CreateDomainResult>(\"/domains\", input);\n }\n\n verify(id: string): Promise<{ status: \"verified\" | \"failed\" }> {\n return this.http.post<{ status: \"verified\" | \"failed\" }>(`/domains/${id}/verify`);\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/domains/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { ListHistoryParams, MessageLog, MessageLogDetail, PaginatedResult } from \"../types.js\";\n\nexport class HistoryResource {\n constructor(private readonly http: HttpClient) {}\n\n list(params?: ListHistoryParams): Promise<PaginatedResult<MessageLog>> {\n const qs = new URLSearchParams();\n if (params?.type) qs.set(\"type\", params.type);\n if (params?.status) qs.set(\"status\", params.status);\n if (params?.from) qs.set(\"from\", params.from);\n if (params?.to) qs.set(\"to\", params.to);\n if (params?.page !== undefined) qs.set(\"page\", String(params.page));\n if (params?.limit !== undefined) qs.set(\"limit\", String(params.limit));\n const query = qs.toString();\n return this.http.get<PaginatedResult<MessageLog>>(`/history${query ? `?${query}` : \"\"}`);\n }\n\n get(id: string): Promise<MessageLogDetail> {\n return this.http.get<MessageLogDetail>(`/history/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { AreaCode, ManagedNumber } from \"../types.js\";\n\nexport interface ProvisionManagedNumberInput {\n areaCode: number;\n name?: string;\n displayName: string;\n otpMethod?: \"SMS\" | \"VOICE\";\n otpLanguage?: string;\n pin?: string;\n orgWabaId?: string;\n wabaId?: string;\n accessToken?: string;\n}\n\nexport interface RetryVerificationInput {\n otpMethod?: \"SMS\" | \"VOICE\";\n otpLanguage?: string;\n}\n\nexport class ManagedNumbersResource {\n constructor(private readonly http: HttpClient) {}\n\n listAreaCodes(): Promise<AreaCode[]> {\n return this.http.get<AreaCode[]>(\"/managed-numbers/area-codes\");\n }\n\n list(): Promise<ManagedNumber[]> {\n return this.http.get<ManagedNumber[]>(\"/managed-numbers\");\n }\n\n get(id: string): Promise<ManagedNumber> {\n return this.http.get<ManagedNumber>(`/managed-numbers/${id}`);\n }\n\n provision(input: ProvisionManagedNumberInput): Promise<ManagedNumber> {\n return this.http.post<ManagedNumber>(\"/managed-numbers\", input);\n }\n\n retryVerification(id: string, input?: RetryVerificationInput): Promise<ManagedNumber> {\n return this.http.post<ManagedNumber>(`/managed-numbers/${id}/retry-verification`, input);\n }\n\n cancel(id: string): Promise<ManagedNumber> {\n return this.http.post<ManagedNumber>(`/managed-numbers/${id}/cancel`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { SendMessageInput, SendMessageResult } from \"../types.js\";\n\nexport class MessagesResource {\n constructor(private readonly http: HttpClient) {}\n\n send(input: SendMessageInput): Promise<SendMessageResult> {\n return this.http.post<SendMessageResult>(\"/messages\", input);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { AvailablePlan, OrgPlan, PlansResult, UpdatePlanInput } from \"../types.js\";\n\nexport class PlansResource {\n constructor(private readonly http: HttpClient) {}\n\n get(): Promise<PlansResult> {\n return this.http.get<PlansResult>(\"/plans\");\n }\n\n update(input: UpdatePlanInput): Promise<OrgPlan> {\n return this.http.put<OrgPlan>(\"/plans\", input);\n }\n\n listAvailable(): Promise<AvailablePlan[]> {\n return this.http.get<AvailablePlan[]>(\"/plans/available\");\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { SmtpConfig, UpsertSmtpConfigInput } from \"../types.js\";\n\nexport class SmtpResource {\n constructor(private readonly http: HttpClient) {}\n\n get(): Promise<SmtpConfig> {\n return this.http.get<SmtpConfig>(\"/smtp-config\");\n }\n\n upsert(input: UpsertSmtpConfigInput): Promise<SmtpConfig> {\n return this.http.put<SmtpConfig>(\"/smtp-config\", input);\n }\n\n delete(): Promise<void> {\n return this.http.delete(\"/smtp-config\");\n }\n\n test(to: string): Promise<{ success: boolean; error?: string }> {\n return this.http.post<{ success: boolean; error?: string }>(\"/smtp-config/test\", { to });\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { StatisticsParams, StatisticsResult } from \"../types.js\";\n\nexport class StatisticsResource {\n constructor(private readonly http: HttpClient) {}\n\n get(params?: StatisticsParams): Promise<StatisticsResult> {\n const qs = new URLSearchParams();\n if (params?.from) qs.set(\"from\", params.from);\n if (params?.to) qs.set(\"to\", params.to);\n if (params?.groupBy) qs.set(\"groupBy\", params.groupBy);\n const query = qs.toString();\n return this.http.get<StatisticsResult>(`/statistics${query ? `?${query}` : \"\"}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateTemplateInput,\n MessageTemplate,\n MessageType,\n SaveDraftInput,\n UpdateTemplateInput,\n} from \"../types.js\";\n\nexport class TemplatesResource {\n constructor(private readonly http: HttpClient) {}\n\n list(params?: { type?: MessageType; isActive?: boolean }): Promise<MessageTemplate[]> {\n const qs = new URLSearchParams();\n if (params?.type) qs.set(\"type\", params.type);\n if (params?.isActive !== undefined) qs.set(\"isActive\", String(params.isActive));\n const query = qs.toString();\n return this.http.get<MessageTemplate[]>(`/templates${query ? `?${query}` : \"\"}`);\n }\n\n get(id: string): Promise<MessageTemplate> {\n return this.http.get<MessageTemplate>(`/templates/${id}`);\n }\n\n create(input: CreateTemplateInput): Promise<MessageTemplate> {\n return this.http.post<MessageTemplate>(\"/templates\", input);\n }\n\n update(id: string, input: UpdateTemplateInput): Promise<MessageTemplate> {\n return this.http.put<MessageTemplate>(`/templates/${id}`, input);\n }\n\n saveDraft(id: string, input: SaveDraftInput): Promise<MessageTemplate> {\n return this.http.patch<MessageTemplate>(`/templates/${id}/draft`, input);\n }\n\n publish(id: string): Promise<MessageTemplate> {\n return this.http.post<MessageTemplate>(`/templates/${id}/publish`);\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/templates/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateWebhookInput,\n OrgWebhook,\n OrgWebhookWithSecret,\n RotateSecretResult,\n UpdateWebhookInput,\n WebhookDelivery,\n} from \"../types.js\";\n\nexport class WebhooksResource {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<OrgWebhook[]> {\n return this.http.get<OrgWebhook[]>(\"/webhooks\");\n }\n\n create(input: CreateWebhookInput): Promise<OrgWebhookWithSecret> {\n return this.http.post<OrgWebhookWithSecret>(\"/webhooks\", input);\n }\n\n update(id: string, input: UpdateWebhookInput): Promise<OrgWebhook> {\n return this.http.put<OrgWebhook>(`/webhooks/${id}`, input);\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/webhooks/${id}`);\n }\n\n listDeliveries(id: string): Promise<WebhookDelivery[]> {\n return this.http.get<WebhookDelivery[]>(`/webhooks/${id}/deliveries`);\n }\n\n rotateSecret(id: string): Promise<RotateSecretResult> {\n return this.http.post<RotateSecretResult>(`/webhooks/${id}/rotate-secret`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n ConnectWabaInput,\n ConnectWhatsappNumberInput,\n OrgWaba,\n PhoneNumberStatusResult,\n RegisterWhatsappNumberInput,\n UpdateWhatsappProfileInput,\n WabaHealthResult,\n WhatsappNumber,\n WhatsappNumberProfile,\n} from \"../types.js\";\n\nexport class WhatsappNumbersResource {\n constructor(private readonly http: HttpClient) {}\n\n // ── WABAs ────────────────────────────────────────────────────────────────\n\n listWabas(): Promise<OrgWaba[]> {\n return this.http.get<OrgWaba[]>(\"/whatsapp/wabas\");\n }\n\n connectWaba(input: ConnectWabaInput): Promise<OrgWaba> {\n return this.http.post<OrgWaba>(\"/whatsapp/wabas/connect\", input);\n }\n\n reconnectWaba(id: string): Promise<OrgWaba> {\n return this.http.post<OrgWaba>(`/whatsapp/wabas/${id}/reconnect`);\n }\n\n disconnectWaba(id: string): Promise<void> {\n return this.http.delete(`/whatsapp/wabas/${id}`);\n }\n\n getWabaHealth(id: string): Promise<WabaHealthResult> {\n return this.http.get<WabaHealthResult>(`/whatsapp/wabas/${id}/health`);\n }\n\n // ── Numbers ──────────────────────────────────────────────────────────────\n\n listNumbers(): Promise<WhatsappNumber[]> {\n return this.http.get<WhatsappNumber[]>(\"/whatsapp/numbers\");\n }\n\n connectNumber(input: ConnectWhatsappNumberInput): Promise<WhatsappNumber> {\n return this.http.post<WhatsappNumber>(\"/whatsapp/numbers/connect\", input);\n }\n\n registerNumber(input: RegisterWhatsappNumberInput): Promise<WhatsappNumber> {\n return this.http.post<WhatsappNumber>(\"/whatsapp/numbers\", input);\n }\n\n verifyCode(id: string, code: string): Promise<WhatsappNumber> {\n return this.http.post<WhatsappNumber>(`/whatsapp/numbers/${id}/verify-code`, { code });\n }\n\n resendCode(id: string): Promise<void> {\n return this.http.post(`/whatsapp/numbers/${id}/resend-code`);\n }\n\n getProfile(id: string): Promise<WhatsappNumberProfile> {\n return this.http.get<WhatsappNumberProfile>(`/whatsapp/numbers/${id}/profile`);\n }\n\n updateProfile(id: string, input: UpdateWhatsappProfileInput): Promise<WhatsappNumberProfile> {\n return this.http.patch<WhatsappNumberProfile>(`/whatsapp/numbers/${id}/profile`, input);\n }\n\n updateDisplayName(id: string, displayName: string): Promise<WhatsappNumber> {\n return this.http.patch<WhatsappNumber>(`/whatsapp/numbers/${id}`, { displayName });\n }\n\n getStatus(id: string): Promise<PhoneNumberStatusResult> {\n return this.http.get<PhoneNumberStatusResult>(`/whatsapp/numbers/${id}/status`);\n }\n\n refreshNumbers(): Promise<WhatsappNumber[]> {\n return this.http.post<WhatsappNumber[]>(\"/whatsapp/numbers/refresh\");\n }\n\n deleteNumber(id: string): Promise<void> {\n return this.http.delete(`/whatsapp/numbers/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateWhatsappTemplateInput,\n SyncWhatsappTemplatesResult,\n UpdateWhatsappTemplateInput,\n WhatsappTemplate,\n} from \"../types.js\";\n\nexport class WhatsappTemplatesResource {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<WhatsappTemplate[]> {\n return this.http.get<WhatsappTemplate[]>(\"/whatsapp/templates\");\n }\n\n create(input: CreateWhatsappTemplateInput): Promise<WhatsappTemplate> {\n return this.http.post<WhatsappTemplate>(\"/whatsapp/templates\", input);\n }\n\n update(id: string, input: UpdateWhatsappTemplateInput): Promise<WhatsappTemplate> {\n return this.http.put<WhatsappTemplate>(`/whatsapp/templates/${id}`, input);\n }\n\n sync(): Promise<SyncWhatsappTemplatesResult> {\n return this.http.post<SyncWhatsappTemplatesResult>(\"/whatsapp/templates/sync\");\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/whatsapp/templates/${id}`);\n }\n}\n","import { HttpClient, type HttpClientOptions } from \"./http.js\";\nimport { DomainsResource } from \"./resources/domains.js\";\nimport { HistoryResource } from \"./resources/history.js\";\nimport { ManagedNumbersResource } from \"./resources/managed-numbers.js\";\nimport { MessagesResource } from \"./resources/messages.js\";\nimport { PlansResource } from \"./resources/plans.js\";\nimport { SmtpResource } from \"./resources/smtp.js\";\nimport { StatisticsResource } from \"./resources/statistics.js\";\nimport { TemplatesResource } from \"./resources/templates.js\";\nimport { WebhooksResource } from \"./resources/webhooks.js\";\nimport { WhatsappNumbersResource } from \"./resources/whatsapp-numbers.js\";\nimport { WhatsappTemplatesResource } from \"./resources/whatsapp-templates.js\";\n\nexport interface PostClientOptions\n extends Omit<HttpClientOptions, \"baseUrl\"> {\n baseUrl?: string;\n}\n\nexport class PostClient {\n private readonly http: HttpClient;\n\n readonly messages: MessagesResource;\n readonly templates: TemplatesResource;\n readonly whatsappTemplates: WhatsappTemplatesResource;\n readonly whatsappNumbers: WhatsappNumbersResource;\n readonly domains: DomainsResource;\n readonly webhooks: WebhooksResource;\n readonly smtp: SmtpResource;\n readonly plans: PlansResource;\n readonly history: HistoryResource;\n readonly statistics: StatisticsResource;\n readonly managedNumbers: ManagedNumbersResource;\n\n constructor(options: HttpClientOptions) {\n this.http = new HttpClient(options);\n\n this.messages = new MessagesResource(this.http);\n this.templates = new TemplatesResource(this.http);\n this.whatsappTemplates = new WhatsappTemplatesResource(this.http);\n this.whatsappNumbers = new WhatsappNumbersResource(this.http);\n this.domains = new DomainsResource(this.http);\n this.webhooks = new WebhooksResource(this.http);\n this.smtp = new SmtpResource(this.http);\n this.plans = new PlansResource(this.http);\n this.history = new HistoryResource(this.http);\n this.statistics = new StatisticsResource(this.http);\n this.managedNumbers = new ManagedNumbersResource(this.http);\n }\n}\n","import type { FastifyBaseLogger, FastifyInstance, FastifyPluginAsync, FastifyRequest } from \"fastify\";\nimport fp from \"fastify-plugin\";\nimport { PostClient } from \"./client.js\";\n\nexport interface ServiceAccountOptions {\n issuerUrl: string;\n clientId: string;\n clientSecret: string;\n}\n\nexport interface PostPluginOptions {\n serviceAccount: ServiceAccountOptions;\n baseUrl?: string;\n logLevel?: \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"silent\";\n}\n\ninterface CachedToken {\n value: string;\n expiresAt: number;\n organizationId: string | undefined;\n}\n\nfunction decodeJwtPayload(token: string): Record<string, unknown> {\n try {\n const [, payload] = token.split(\".\");\n if (!payload) return {};\n const json = Buffer.from(payload, \"base64url\").toString(\"utf-8\");\n return JSON.parse(json) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nclass TokenProvider {\n private readonly issuerUrl: string;\n private readonly clientId: string;\n private readonly clientSecret: string;\n private readonly log: FastifyBaseLogger;\n private cached: CachedToken | null = null;\n private inflight: Promise<CachedToken> | null = null;\n\n constructor(options: PostPluginOptions, log: FastifyBaseLogger) {\n this.issuerUrl = options.serviceAccount.issuerUrl.replace(/\\/$/, \"\");\n this.clientId = options.serviceAccount.clientId;\n this.clientSecret = options.serviceAccount.clientSecret;\n this.log = log;\n }\n\n private async ensureFresh(): Promise<CachedToken> {\n const now = Date.now();\n if (this.cached && this.cached.expiresAt - now > 30_000) return this.cached;\n if (this.inflight) return this.inflight;\n this.inflight = this.fetchToken().finally(() => { this.inflight = null; });\n return this.inflight;\n }\n\n getToken(): Promise<string> {\n return this.ensureFresh().then((c) => c.value);\n }\n\n getOrganizationId(): Promise<string | undefined> {\n return this.ensureFresh().then((c) => c.organizationId);\n }\n\n private async fetchToken(): Promise<CachedToken> {\n this.log.debug(\"[@geekapps/post-fastify] fetching service account token\");\n\n const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString(\"base64\");\n const res = await fetch(`${this.issuerUrl}/auth/token`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${credentials}`,\n },\n body: JSON.stringify({ grant_type: \"service_account\" }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({})) as { error?: string };\n throw new Error(`[@geekapps/post-fastify] auth failed (${res.status}): ${err.error ?? \"unknown error\"}`);\n }\n\n const data = await res.json() as { access_token: string; expires_in?: number };\n const claims = decodeJwtPayload(data.access_token);\n const organizationId =\n typeof claims.organizationId === \"string\" ? claims.organizationId :\n typeof claims.organization_id === \"string\" ? claims.organization_id :\n undefined;\n\n this.cached = {\n value: data.access_token,\n expiresAt: Date.now() + (data.expires_in ?? 420) * 1000,\n organizationId,\n };\n\n this.log.debug({ organizationId, expiresIn: data.expires_in }, \"[@geekapps/post-fastify] token acquired\");\n return this.cached;\n }\n}\n\ndeclare module \"fastify\" {\n interface FastifyInstance {\n postClient: PostClient;\n }\n interface FastifyRequest {\n postClient: PostClient;\n }\n}\n\nconst postPlugin: FastifyPluginAsync<PostPluginOptions> = async (\n fastify: FastifyInstance,\n options: PostPluginOptions,\n) => {\n const log = options.logLevel\n ? fastify.log.child({ level: options.logLevel })\n : fastify.log;\n\n const baseUrl = options.baseUrl ?? \"https://post-api.geekapps.com.br\";\n const provider = new TokenProvider(options, log);\n\n const client = new PostClient({\n baseUrl,\n token: () => provider.getToken(),\n organizationId: () => provider.getOrganizationId() as Promise<string>,\n });\n\n fastify.decorate(\"postClient\", client);\n\n fastify.addHook(\"onRequest\", async (request: FastifyRequest) => {\n request.postClient = client;\n });\n};\n\nexport default fp(postPlugin, {\n fastify: \">=4.0.0\",\n name: \"@geekapps/post-fastify\",\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/domains.ts","../src/resources/history.ts","../src/resources/managed-numbers.ts","../src/resources/messages.ts","../src/resources/plans.ts","../src/resources/smtp.ts","../src/resources/statistics.ts","../src/resources/templates.ts","../src/resources/webhooks.ts","../src/resources/whatsapp-numbers.ts","../src/resources/whatsapp-templates.ts","../src/client.ts","../src/plugin.ts"],"sourcesContent":["export class PostApiError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n public readonly detail?: string,\n ) {\n super(detail ? `${message}: ${detail}` : message);\n this.name = \"PostApiError\";\n }\n\n get isNotFound() {\n return this.status === 404;\n }\n\n get isUnauthorized() {\n return this.status === 401;\n }\n\n get isForbidden() {\n return this.status === 403;\n }\n\n get isRateLimited() {\n return this.status === 429;\n }\n\n get isValidationError() {\n return this.status === 400;\n }\n}\n","import { PostApiError } from \"./errors.js\";\r\n\r\nexport type TokenResolver = string | (() => string | Promise<string>);\r\n\r\nexport interface HttpClientOptions {\r\n baseUrl: string;\r\n token: TokenResolver;\r\n organizationId?: string | (() => string | Promise<string>);\r\n}\r\n\r\nexport class HttpClient {\r\n private readonly baseUrl: string;\r\n private readonly token: TokenResolver;\r\n private readonly organizationId?: string | (() => string | Promise<string>);\r\n\r\n constructor(options: HttpClientOptions) {\r\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\r\n this.token = options.token;\r\n this.organizationId = options.organizationId;\r\n }\r\n\r\n private async resolveToken(): Promise<string> {\r\n return typeof this.token === \"function\" ? await this.token() : this.token;\r\n }\r\n\r\n private async resolveOrgId(): Promise<string | undefined> {\r\n if (!this.organizationId) return undefined;\r\n return typeof this.organizationId === \"function\"\r\n ? await this.organizationId()\r\n : this.organizationId;\r\n }\r\n\r\n async request<T = unknown>(\r\n method: string,\r\n path: string,\r\n body?: unknown,\r\n ): Promise<T> {\r\n const token = await this.resolveToken();\r\n const orgId = await this.resolveOrgId();\r\n\r\n const res = await fetch(`${this.baseUrl}/v1${path}`, {\r\n method,\r\n headers: {\r\n Authorization: `Bearer ${token}`,\r\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\r\n ...(orgId ? { \"X-Organization-Id\": orgId } : {}),\r\n },\r\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n cache: \"no-store\" as any,\r\n });\r\n\r\n if (!res.ok) {\r\n const err = await res.json().catch(() => ({})) as {\r\n error?: string;\r\n detail?: string;\r\n };\r\n throw new PostApiError(\r\n res.status,\r\n err.error ?? `HTTP ${res.status}`,\r\n err.detail,\r\n );\r\n }\r\n\r\n if (res.status === 204) return undefined as T;\r\n return res.json() as Promise<T>;\r\n }\r\n\r\n get<T>(path: string) {\r\n return this.request<T>(\"GET\", path);\r\n }\r\n\r\n post<T>(path: string, body?: unknown) {\r\n return this.request<T>(\"POST\", path, body);\r\n }\r\n\r\n put<T>(path: string, body?: unknown) {\r\n return this.request<T>(\"PUT\", path, body);\r\n }\r\n\r\n patch<T>(path: string, body?: unknown) {\r\n return this.request<T>(\"PATCH\", path, body);\r\n }\r\n\r\n delete<T = void>(path: string) {\r\n return this.request<T>(\"DELETE\", path);\r\n }\r\n}\r\n","import type { HttpClient } from \"../http.js\";\nimport type { CreateDomainInput, CreateDomainResult, OrgDomain } from \"../types.js\";\n\nexport class DomainsResource {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<OrgDomain[]> {\n return this.http.get<OrgDomain[]>(\"/domains\");\n }\n\n get(id: string): Promise<OrgDomain> {\n return this.http.get<OrgDomain>(`/domains/${id}`);\n }\n\n create(input: CreateDomainInput): Promise<CreateDomainResult> {\n return this.http.post<CreateDomainResult>(\"/domains\", input);\n }\n\n verify(id: string): Promise<{ status: \"verified\" | \"failed\" }> {\n return this.http.post<{ status: \"verified\" | \"failed\" }>(`/domains/${id}/verify`);\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/domains/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { ListHistoryParams, MessageLog, MessageLogDetail, PaginatedResult } from \"../types.js\";\n\nexport class HistoryResource {\n constructor(private readonly http: HttpClient) {}\n\n list(params?: ListHistoryParams): Promise<PaginatedResult<MessageLog>> {\n const qs = new URLSearchParams();\n if (params?.type) qs.set(\"type\", params.type);\n if (params?.status) qs.set(\"status\", params.status);\n if (params?.from) qs.set(\"from\", params.from);\n if (params?.to) qs.set(\"to\", params.to);\n if (params?.page !== undefined) qs.set(\"page\", String(params.page));\n if (params?.limit !== undefined) qs.set(\"limit\", String(params.limit));\n const query = qs.toString();\n return this.http.get<PaginatedResult<MessageLog>>(`/history${query ? `?${query}` : \"\"}`);\n }\n\n get(id: string): Promise<MessageLogDetail> {\n return this.http.get<MessageLogDetail>(`/history/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { AreaCode, ManagedNumber } from \"../types.js\";\n\nexport interface ProvisionManagedNumberInput {\n areaCode: number;\n name?: string;\n displayName: string;\n otpMethod?: \"SMS\" | \"VOICE\";\n otpLanguage?: string;\n pin?: string;\n orgWabaId?: string;\n wabaId?: string;\n accessToken?: string;\n}\n\nexport interface RetryVerificationInput {\n otpMethod?: \"SMS\" | \"VOICE\";\n otpLanguage?: string;\n}\n\nexport class ManagedNumbersResource {\n constructor(private readonly http: HttpClient) {}\n\n listAreaCodes(): Promise<AreaCode[]> {\n return this.http.get<AreaCode[]>(\"/managed-numbers/area-codes\");\n }\n\n list(): Promise<ManagedNumber[]> {\n return this.http.get<ManagedNumber[]>(\"/managed-numbers\");\n }\n\n get(id: string): Promise<ManagedNumber> {\n return this.http.get<ManagedNumber>(`/managed-numbers/${id}`);\n }\n\n provision(input: ProvisionManagedNumberInput): Promise<ManagedNumber> {\n return this.http.post<ManagedNumber>(\"/managed-numbers\", input);\n }\n\n retryVerification(id: string, input?: RetryVerificationInput): Promise<ManagedNumber> {\n return this.http.post<ManagedNumber>(`/managed-numbers/${id}/retry-verification`, input);\n }\n\n cancel(id: string): Promise<ManagedNumber> {\n return this.http.post<ManagedNumber>(`/managed-numbers/${id}/cancel`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { SendMessageInput, SendMessageResult } from \"../types.js\";\n\nexport class MessagesResource {\n constructor(private readonly http: HttpClient) {}\n\n send(input: SendMessageInput): Promise<SendMessageResult> {\n return this.http.post<SendMessageResult>(\"/messages\", input);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { AvailablePlan, OrgPlan, PlansResult, UpdatePlanInput } from \"../types.js\";\n\nexport class PlansResource {\n constructor(private readonly http: HttpClient) {}\n\n get(): Promise<PlansResult> {\n return this.http.get<PlansResult>(\"/plans\");\n }\n\n update(input: UpdatePlanInput): Promise<OrgPlan> {\n return this.http.put<OrgPlan>(\"/plans\", input);\n }\n\n listAvailable(): Promise<AvailablePlan[]> {\n return this.http.get<AvailablePlan[]>(\"/plans/available\");\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { SmtpConfig, UpsertSmtpConfigInput } from \"../types.js\";\n\nexport class SmtpResource {\n constructor(private readonly http: HttpClient) {}\n\n get(): Promise<SmtpConfig> {\n return this.http.get<SmtpConfig>(\"/smtp-config\");\n }\n\n upsert(input: UpsertSmtpConfigInput): Promise<SmtpConfig> {\n return this.http.put<SmtpConfig>(\"/smtp-config\", input);\n }\n\n delete(): Promise<void> {\n return this.http.delete(\"/smtp-config\");\n }\n\n test(to: string): Promise<{ success: boolean; error?: string }> {\n return this.http.post<{ success: boolean; error?: string }>(\"/smtp-config/test\", { to });\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { StatisticsParams, StatisticsResult } from \"../types.js\";\n\nexport class StatisticsResource {\n constructor(private readonly http: HttpClient) {}\n\n get(params?: StatisticsParams): Promise<StatisticsResult> {\n const qs = new URLSearchParams();\n if (params?.from) qs.set(\"from\", params.from);\n if (params?.to) qs.set(\"to\", params.to);\n if (params?.groupBy) qs.set(\"groupBy\", params.groupBy);\n const query = qs.toString();\n return this.http.get<StatisticsResult>(`/statistics${query ? `?${query}` : \"\"}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateTemplateInput,\n MessageTemplate,\n MessageType,\n SaveDraftInput,\n UpdateTemplateInput,\n} from \"../types.js\";\n\nexport class TemplatesResource {\n constructor(private readonly http: HttpClient) {}\n\n list(params?: { type?: MessageType; isActive?: boolean }): Promise<MessageTemplate[]> {\n const qs = new URLSearchParams();\n if (params?.type) qs.set(\"type\", params.type);\n if (params?.isActive !== undefined) qs.set(\"isActive\", String(params.isActive));\n const query = qs.toString();\n return this.http.get<MessageTemplate[]>(`/templates${query ? `?${query}` : \"\"}`);\n }\n\n get(id: string): Promise<MessageTemplate> {\n return this.http.get<MessageTemplate>(`/templates/${id}`);\n }\n\n create(input: CreateTemplateInput): Promise<MessageTemplate> {\n return this.http.post<MessageTemplate>(\"/templates\", input);\n }\n\n update(id: string, input: UpdateTemplateInput): Promise<MessageTemplate> {\n return this.http.put<MessageTemplate>(`/templates/${id}`, input);\n }\n\n saveDraft(id: string, input: SaveDraftInput): Promise<MessageTemplate> {\n return this.http.patch<MessageTemplate>(`/templates/${id}/draft`, input);\n }\n\n publish(id: string): Promise<MessageTemplate> {\n return this.http.post<MessageTemplate>(`/templates/${id}/publish`);\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/templates/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateWebhookInput,\n OrgWebhook,\n OrgWebhookWithSecret,\n RotateSecretResult,\n UpdateWebhookInput,\n WebhookDelivery,\n} from \"../types.js\";\n\nexport class WebhooksResource {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<OrgWebhook[]> {\n return this.http.get<OrgWebhook[]>(\"/webhooks\");\n }\n\n create(input: CreateWebhookInput): Promise<OrgWebhookWithSecret> {\n return this.http.post<OrgWebhookWithSecret>(\"/webhooks\", input);\n }\n\n update(id: string, input: UpdateWebhookInput): Promise<OrgWebhook> {\n return this.http.put<OrgWebhook>(`/webhooks/${id}`, input);\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/webhooks/${id}`);\n }\n\n listDeliveries(id: string): Promise<WebhookDelivery[]> {\n return this.http.get<WebhookDelivery[]>(`/webhooks/${id}/deliveries`);\n }\n\n rotateSecret(id: string): Promise<RotateSecretResult> {\n return this.http.post<RotateSecretResult>(`/webhooks/${id}/rotate-secret`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n ConnectWabaInput,\n ConnectWhatsappNumberInput,\n OrgWaba,\n PhoneNumberStatusResult,\n RegisterWhatsappNumberInput,\n UpdateWhatsappProfileInput,\n WabaHealthResult,\n WhatsappNumber,\n WhatsappNumberProfile,\n} from \"../types.js\";\n\nexport class WhatsappNumbersResource {\n constructor(private readonly http: HttpClient) {}\n\n // ── WABAs ────────────────────────────────────────────────────────────────\n\n listWabas(): Promise<OrgWaba[]> {\n return this.http.get<OrgWaba[]>(\"/whatsapp/wabas\");\n }\n\n connectWaba(input: ConnectWabaInput): Promise<OrgWaba> {\n return this.http.post<OrgWaba>(\"/whatsapp/wabas/connect\", input);\n }\n\n reconnectWaba(id: string): Promise<OrgWaba> {\n return this.http.post<OrgWaba>(`/whatsapp/wabas/${id}/reconnect`);\n }\n\n disconnectWaba(id: string): Promise<void> {\n return this.http.delete(`/whatsapp/wabas/${id}`);\n }\n\n getWabaHealth(id: string): Promise<WabaHealthResult> {\n return this.http.get<WabaHealthResult>(`/whatsapp/wabas/${id}/health`);\n }\n\n // ── Numbers ──────────────────────────────────────────────────────────────\n\n listNumbers(): Promise<WhatsappNumber[]> {\n return this.http.get<WhatsappNumber[]>(\"/whatsapp/numbers\");\n }\n\n connectNumber(input: ConnectWhatsappNumberInput): Promise<WhatsappNumber> {\n return this.http.post<WhatsappNumber>(\"/whatsapp/numbers/connect\", input);\n }\n\n registerNumber(input: RegisterWhatsappNumberInput): Promise<WhatsappNumber> {\n return this.http.post<WhatsappNumber>(\"/whatsapp/numbers\", input);\n }\n\n verifyCode(id: string, code: string): Promise<WhatsappNumber> {\n return this.http.post<WhatsappNumber>(`/whatsapp/numbers/${id}/verify-code`, { code });\n }\n\n resendCode(id: string): Promise<void> {\n return this.http.post(`/whatsapp/numbers/${id}/resend-code`);\n }\n\n getProfile(id: string): Promise<WhatsappNumberProfile> {\n return this.http.get<WhatsappNumberProfile>(`/whatsapp/numbers/${id}/profile`);\n }\n\n updateProfile(id: string, input: UpdateWhatsappProfileInput): Promise<WhatsappNumberProfile> {\n return this.http.patch<WhatsappNumberProfile>(`/whatsapp/numbers/${id}/profile`, input);\n }\n\n updateDisplayName(id: string, displayName: string): Promise<WhatsappNumber> {\n return this.http.patch<WhatsappNumber>(`/whatsapp/numbers/${id}`, { displayName });\n }\n\n getStatus(id: string): Promise<PhoneNumberStatusResult> {\n return this.http.get<PhoneNumberStatusResult>(`/whatsapp/numbers/${id}/status`);\n }\n\n refreshNumbers(): Promise<WhatsappNumber[]> {\n return this.http.post<WhatsappNumber[]>(\"/whatsapp/numbers/refresh\");\n }\n\n deleteNumber(id: string): Promise<void> {\n return this.http.delete(`/whatsapp/numbers/${id}`);\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateWhatsappTemplateInput,\n SyncWhatsappTemplatesResult,\n UpdateWhatsappTemplateInput,\n WhatsappTemplate,\n} from \"../types.js\";\n\nexport class WhatsappTemplatesResource {\n constructor(private readonly http: HttpClient) {}\n\n list(): Promise<WhatsappTemplate[]> {\n return this.http.get<WhatsappTemplate[]>(\"/whatsapp/templates\");\n }\n\n create(input: CreateWhatsappTemplateInput): Promise<WhatsappTemplate> {\n return this.http.post<WhatsappTemplate>(\"/whatsapp/templates\", input);\n }\n\n update(id: string, input: UpdateWhatsappTemplateInput): Promise<WhatsappTemplate> {\n return this.http.put<WhatsappTemplate>(`/whatsapp/templates/${id}`, input);\n }\n\n sync(): Promise<SyncWhatsappTemplatesResult> {\n return this.http.post<SyncWhatsappTemplatesResult>(\"/whatsapp/templates/sync\");\n }\n\n delete(id: string): Promise<void> {\n return this.http.delete(`/whatsapp/templates/${id}`);\n }\n}\n","import { HttpClient, type HttpClientOptions } from \"./http.js\";\nimport { DomainsResource } from \"./resources/domains.js\";\nimport { HistoryResource } from \"./resources/history.js\";\nimport { ManagedNumbersResource } from \"./resources/managed-numbers.js\";\nimport { MessagesResource } from \"./resources/messages.js\";\nimport { PlansResource } from \"./resources/plans.js\";\nimport { SmtpResource } from \"./resources/smtp.js\";\nimport { StatisticsResource } from \"./resources/statistics.js\";\nimport { TemplatesResource } from \"./resources/templates.js\";\nimport { WebhooksResource } from \"./resources/webhooks.js\";\nimport { WhatsappNumbersResource } from \"./resources/whatsapp-numbers.js\";\nimport { WhatsappTemplatesResource } from \"./resources/whatsapp-templates.js\";\n\nexport interface PostClientOptions\n extends Omit<HttpClientOptions, \"baseUrl\"> {\n baseUrl?: string;\n}\n\nexport class PostClient {\n private readonly http: HttpClient;\n\n readonly messages: MessagesResource;\n readonly templates: TemplatesResource;\n readonly whatsappTemplates: WhatsappTemplatesResource;\n readonly whatsappNumbers: WhatsappNumbersResource;\n readonly domains: DomainsResource;\n readonly webhooks: WebhooksResource;\n readonly smtp: SmtpResource;\n readonly plans: PlansResource;\n readonly history: HistoryResource;\n readonly statistics: StatisticsResource;\n readonly managedNumbers: ManagedNumbersResource;\n\n constructor(optionsOrHttp: HttpClientOptions | HttpClient) {\n this.http = optionsOrHttp instanceof HttpClient ? optionsOrHttp : new HttpClient(optionsOrHttp);\n\n this.messages = new MessagesResource(this.http);\n this.templates = new TemplatesResource(this.http);\n this.whatsappTemplates = new WhatsappTemplatesResource(this.http);\n this.whatsappNumbers = new WhatsappNumbersResource(this.http);\n this.domains = new DomainsResource(this.http);\n this.webhooks = new WebhooksResource(this.http);\n this.smtp = new SmtpResource(this.http);\n this.plans = new PlansResource(this.http);\n this.history = new HistoryResource(this.http);\n this.statistics = new StatisticsResource(this.http);\n this.managedNumbers = new ManagedNumbersResource(this.http);\n }\n}\n","import type { FastifyBaseLogger, FastifyInstance, FastifyPluginAsync, FastifyRequest } from \"fastify\";\nimport fp from \"fastify-plugin\";\nimport { PostClient } from \"./client.js\";\nimport { HttpClient } from \"./http.js\";\n\nexport interface ServiceAccountOptions {\n issuerUrl: string;\n clientId: string;\n clientSecret: string;\n}\n\nexport interface PostPluginOptions {\n serviceAccount: ServiceAccountOptions;\n baseUrl?: string;\n logLevel?: \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"silent\";\n}\n\ninterface CachedToken {\n value: string;\n expiresAt: number;\n organizationId: string | undefined;\n}\n\ntype Logger = Pick<FastifyBaseLogger, \"debug\" | \"info\" | \"warn\" | \"error\">;\n\nfunction log(logger: Logger, msg: string, data?: Record<string, unknown>) {\n if (data) {\n process.stdout.write(`[geekapps-post] ${msg} ${JSON.stringify(data, null, 2)}\\n`);\n } else {\n process.stdout.write(`[geekapps-post] ${msg}\\n`);\n }\n}\n\nfunction decodeJwtPayload(token: string): Record<string, unknown> {\n try {\n const [, payload] = token.split(\".\");\n if (!payload) return {};\n const json = Buffer.from(payload, \"base64url\").toString(\"utf-8\");\n return JSON.parse(json) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nclass TokenProvider {\n private readonly issuerUrl: string;\n private readonly clientId: string;\n private readonly clientSecret: string;\n private readonly logger: Logger;\n private cached: CachedToken | null = null;\n private inflight: Promise<CachedToken> | null = null;\n\n constructor(options: PostPluginOptions, logger: Logger) {\n this.issuerUrl = options.serviceAccount.issuerUrl.replace(/\\/$/, \"\");\n this.clientId = options.serviceAccount.clientId;\n this.clientSecret = options.serviceAccount.clientSecret;\n this.logger = logger;\n }\n\n private async ensureFresh(): Promise<CachedToken> {\n const now = Date.now();\n if (this.cached && this.cached.expiresAt - now > 30_000) {\n log(this.logger, \"service account token cache hit\", {\n expiresAt: new Date(this.cached.expiresAt).toISOString(),\n });\n return this.cached;\n }\n log(this.logger, \"service account token cache miss — fetching new token\");\n if (this.inflight) return this.inflight;\n this.inflight = this.fetchToken().finally(() => { this.inflight = null; });\n return this.inflight;\n }\n\n getToken(): Promise<string> {\n return this.ensureFresh().then((c) => c.value);\n }\n\n getOrganizationId(): Promise<string | undefined> {\n return this.ensureFresh().then((c) => c.organizationId);\n }\n\n private async fetchToken(): Promise<CachedToken> {\n log(this.logger, \"fetching service account token\", {\n url: `${this.issuerUrl}/auth/token`,\n clientId: this.clientId,\n });\n\n const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString(\"base64\");\n const res = await fetch(`${this.issuerUrl}/auth/token`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${credentials}`,\n },\n body: JSON.stringify({ grant_type: \"service_account\" }),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({})) as { error?: string };\n const msg = `auth failed (${res.status}): ${err.error ?? \"unknown error\"}`;\n log(this.logger, msg);\n throw new Error(`[geekapps-post] ${msg}`);\n }\n\n const data = await res.json() as { access_token: string; expires_in?: number };\n const claims = decodeJwtPayload(data.access_token);\n const organizationId =\n typeof claims.organizationId === \"string\" ? claims.organizationId :\n typeof claims.organization_id === \"string\" ? claims.organization_id :\n undefined;\n\n this.cached = {\n value: data.access_token,\n expiresAt: Date.now() + (data.expires_in ?? 420) * 1000,\n organizationId,\n };\n\n log(this.logger, \"service account token acquired\", {\n claims: { organizationId },\n expiresIn: `${data.expires_in ?? 420}s`,\n });\n\n return this.cached;\n }\n}\n\nexport class LoggingHttpClient extends HttpClient {\n private readonly logger: Logger;\n private readonly baseUrlLog: string;\n\n constructor(options: ConstructorParameters<typeof HttpClient>[0], logger: Logger) {\n super(options);\n this.logger = logger;\n this.baseUrlLog = options.baseUrl.replace(/\\/$/, \"\");\n }\n\n override async request<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrlLog}/v1${path}`;\n log(this.logger, \"api request\", { method, url });\n if (body !== undefined) {\n log(this.logger, \"api request body\", { body });\n }\n try {\n const result = await super.request<T>(method, path, body);\n log(this.logger, \"api response\", { method, url, status: 200 });\n return result;\n } catch (err) {\n const status = (err as { status?: number }).status;\n log(this.logger, \"api response error\", { method, url, status });\n throw err;\n }\n }\n}\n\ndeclare module \"fastify\" {\n interface FastifyInstance {\n postClient: PostClient;\n }\n interface FastifyRequest {\n postClient: PostClient;\n }\n}\n\nconst postPlugin: FastifyPluginAsync<PostPluginOptions> = async (\n fastify: FastifyInstance,\n options: PostPluginOptions,\n) => {\n const logger = options.logLevel\n ? fastify.log.child({ level: options.logLevel })\n : fastify.log;\n\n const baseUrl = options.baseUrl ?? \"https://post-api.geekapps.com.br\";\n const provider = new TokenProvider(options, logger);\n\n log(logger, \"geekapps post initialized\", {\n issuerUrl: options.serviceAccount.issuerUrl,\n baseUrl,\n serviceAccount: true,\n });\n\n const http = new LoggingHttpClient(\n {\n baseUrl,\n token: () => provider.getToken(),\n organizationId: () => provider.getOrganizationId() as Promise<string>,\n },\n logger,\n );\n\n const client = new PostClient(http);\n\n fastify.decorate(\"postClient\", client);\n\n fastify.addHook(\"onRequest\", async (request: FastifyRequest) => {\n request.postClient = client;\n });\n};\n\nexport default fp(postPlugin, {\n fastify: \">=4.0.0\",\n name: \"@geekapps/post-fastify\",\n});\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -31,8 +31,8 @@ export class PostClient {
|
|
|
31
31
|
readonly statistics: StatisticsResource;
|
|
32
32
|
readonly managedNumbers: ManagedNumbersResource;
|
|
33
33
|
|
|
34
|
-
constructor(
|
|
35
|
-
this.http = new HttpClient(
|
|
34
|
+
constructor(optionsOrHttp: HttpClientOptions | HttpClient) {
|
|
35
|
+
this.http = optionsOrHttp instanceof HttpClient ? optionsOrHttp : new HttpClient(optionsOrHttp);
|
|
36
36
|
|
|
37
37
|
this.messages = new MessagesResource(this.http);
|
|
38
38
|
this.templates = new TemplatesResource(this.http);
|
package/src/plugin.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { FastifyBaseLogger, FastifyInstance, FastifyPluginAsync, FastifyRequest } from "fastify";
|
|
2
2
|
import fp from "fastify-plugin";
|
|
3
3
|
import { PostClient } from "./client.js";
|
|
4
|
+
import { HttpClient } from "./http.js";
|
|
4
5
|
|
|
5
6
|
export interface ServiceAccountOptions {
|
|
6
7
|
issuerUrl: string;
|
|
@@ -20,6 +21,16 @@ interface CachedToken {
|
|
|
20
21
|
organizationId: string | undefined;
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
type Logger = Pick<FastifyBaseLogger, "debug" | "info" | "warn" | "error">;
|
|
25
|
+
|
|
26
|
+
function log(logger: Logger, msg: string, data?: Record<string, unknown>) {
|
|
27
|
+
if (data) {
|
|
28
|
+
process.stdout.write(`[geekapps-post] ${msg} ${JSON.stringify(data, null, 2)}\n`);
|
|
29
|
+
} else {
|
|
30
|
+
process.stdout.write(`[geekapps-post] ${msg}\n`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
23
34
|
function decodeJwtPayload(token: string): Record<string, unknown> {
|
|
24
35
|
try {
|
|
25
36
|
const [, payload] = token.split(".");
|
|
@@ -35,20 +46,26 @@ class TokenProvider {
|
|
|
35
46
|
private readonly issuerUrl: string;
|
|
36
47
|
private readonly clientId: string;
|
|
37
48
|
private readonly clientSecret: string;
|
|
38
|
-
private readonly
|
|
49
|
+
private readonly logger: Logger;
|
|
39
50
|
private cached: CachedToken | null = null;
|
|
40
51
|
private inflight: Promise<CachedToken> | null = null;
|
|
41
52
|
|
|
42
|
-
constructor(options: PostPluginOptions,
|
|
53
|
+
constructor(options: PostPluginOptions, logger: Logger) {
|
|
43
54
|
this.issuerUrl = options.serviceAccount.issuerUrl.replace(/\/$/, "");
|
|
44
55
|
this.clientId = options.serviceAccount.clientId;
|
|
45
56
|
this.clientSecret = options.serviceAccount.clientSecret;
|
|
46
|
-
this.
|
|
57
|
+
this.logger = logger;
|
|
47
58
|
}
|
|
48
59
|
|
|
49
60
|
private async ensureFresh(): Promise<CachedToken> {
|
|
50
61
|
const now = Date.now();
|
|
51
|
-
if (this.cached && this.cached.expiresAt - now > 30_000)
|
|
62
|
+
if (this.cached && this.cached.expiresAt - now > 30_000) {
|
|
63
|
+
log(this.logger, "service account token cache hit", {
|
|
64
|
+
expiresAt: new Date(this.cached.expiresAt).toISOString(),
|
|
65
|
+
});
|
|
66
|
+
return this.cached;
|
|
67
|
+
}
|
|
68
|
+
log(this.logger, "service account token cache miss — fetching new token");
|
|
52
69
|
if (this.inflight) return this.inflight;
|
|
53
70
|
this.inflight = this.fetchToken().finally(() => { this.inflight = null; });
|
|
54
71
|
return this.inflight;
|
|
@@ -63,7 +80,10 @@ class TokenProvider {
|
|
|
63
80
|
}
|
|
64
81
|
|
|
65
82
|
private async fetchToken(): Promise<CachedToken> {
|
|
66
|
-
this.
|
|
83
|
+
log(this.logger, "fetching service account token", {
|
|
84
|
+
url: `${this.issuerUrl}/auth/token`,
|
|
85
|
+
clientId: this.clientId,
|
|
86
|
+
});
|
|
67
87
|
|
|
68
88
|
const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString("base64");
|
|
69
89
|
const res = await fetch(`${this.issuerUrl}/auth/token`, {
|
|
@@ -77,7 +97,9 @@ class TokenProvider {
|
|
|
77
97
|
|
|
78
98
|
if (!res.ok) {
|
|
79
99
|
const err = await res.json().catch(() => ({})) as { error?: string };
|
|
80
|
-
|
|
100
|
+
const msg = `auth failed (${res.status}): ${err.error ?? "unknown error"}`;
|
|
101
|
+
log(this.logger, msg);
|
|
102
|
+
throw new Error(`[geekapps-post] ${msg}`);
|
|
81
103
|
}
|
|
82
104
|
|
|
83
105
|
const data = await res.json() as { access_token: string; expires_in?: number };
|
|
@@ -93,11 +115,43 @@ class TokenProvider {
|
|
|
93
115
|
organizationId,
|
|
94
116
|
};
|
|
95
117
|
|
|
96
|
-
|
|
118
|
+
log(this.logger, "service account token acquired", {
|
|
119
|
+
claims: { organizationId },
|
|
120
|
+
expiresIn: `${data.expires_in ?? 420}s`,
|
|
121
|
+
});
|
|
122
|
+
|
|
97
123
|
return this.cached;
|
|
98
124
|
}
|
|
99
125
|
}
|
|
100
126
|
|
|
127
|
+
export class LoggingHttpClient extends HttpClient {
|
|
128
|
+
private readonly logger: Logger;
|
|
129
|
+
private readonly baseUrlLog: string;
|
|
130
|
+
|
|
131
|
+
constructor(options: ConstructorParameters<typeof HttpClient>[0], logger: Logger) {
|
|
132
|
+
super(options);
|
|
133
|
+
this.logger = logger;
|
|
134
|
+
this.baseUrlLog = options.baseUrl.replace(/\/$/, "");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
override async request<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {
|
|
138
|
+
const url = `${this.baseUrlLog}/v1${path}`;
|
|
139
|
+
log(this.logger, "api request", { method, url });
|
|
140
|
+
if (body !== undefined) {
|
|
141
|
+
log(this.logger, "api request body", { body });
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const result = await super.request<T>(method, path, body);
|
|
145
|
+
log(this.logger, "api response", { method, url, status: 200 });
|
|
146
|
+
return result;
|
|
147
|
+
} catch (err) {
|
|
148
|
+
const status = (err as { status?: number }).status;
|
|
149
|
+
log(this.logger, "api response error", { method, url, status });
|
|
150
|
+
throw err;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
101
155
|
declare module "fastify" {
|
|
102
156
|
interface FastifyInstance {
|
|
103
157
|
postClient: PostClient;
|
|
@@ -111,19 +165,30 @@ const postPlugin: FastifyPluginAsync<PostPluginOptions> = async (
|
|
|
111
165
|
fastify: FastifyInstance,
|
|
112
166
|
options: PostPluginOptions,
|
|
113
167
|
) => {
|
|
114
|
-
const
|
|
168
|
+
const logger = options.logLevel
|
|
115
169
|
? fastify.log.child({ level: options.logLevel })
|
|
116
170
|
: fastify.log;
|
|
117
171
|
|
|
118
172
|
const baseUrl = options.baseUrl ?? "https://post-api.geekapps.com.br";
|
|
119
|
-
const provider = new TokenProvider(options,
|
|
173
|
+
const provider = new TokenProvider(options, logger);
|
|
120
174
|
|
|
121
|
-
|
|
175
|
+
log(logger, "geekapps post initialized", {
|
|
176
|
+
issuerUrl: options.serviceAccount.issuerUrl,
|
|
122
177
|
baseUrl,
|
|
123
|
-
|
|
124
|
-
organizationId: () => provider.getOrganizationId() as Promise<string>,
|
|
178
|
+
serviceAccount: true,
|
|
125
179
|
});
|
|
126
180
|
|
|
181
|
+
const http = new LoggingHttpClient(
|
|
182
|
+
{
|
|
183
|
+
baseUrl,
|
|
184
|
+
token: () => provider.getToken(),
|
|
185
|
+
organizationId: () => provider.getOrganizationId() as Promise<string>,
|
|
186
|
+
},
|
|
187
|
+
logger,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const client = new PostClient(http);
|
|
191
|
+
|
|
127
192
|
fastify.decorate("postClient", client);
|
|
128
193
|
|
|
129
194
|
fastify.addHook("onRequest", async (request: FastifyRequest) => {
|