@hasna/microservices 0.0.5 → 0.0.6
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/bin/index.js +9 -1
- package/bin/mcp.js +9 -1
- package/dist/index.js +9 -1
- package/microservices/microservice-company/package.json +27 -0
- package/microservices/microservice-company/src/cli/index.ts +1126 -0
- package/microservices/microservice-company/src/db/company.ts +854 -0
- package/microservices/microservice-company/src/db/database.ts +93 -0
- package/microservices/microservice-company/src/db/migrations.ts +214 -0
- package/microservices/microservice-company/src/db/workflow-migrations.ts +44 -0
- package/microservices/microservice-company/src/index.ts +60 -0
- package/microservices/microservice-company/src/lib/audit.ts +168 -0
- package/microservices/microservice-company/src/lib/finance.ts +299 -0
- package/microservices/microservice-company/src/lib/settings.ts +85 -0
- package/microservices/microservice-company/src/lib/workflows.ts +698 -0
- package/microservices/microservice-company/src/mcp/index.ts +991 -0
- package/microservices/microservice-domains/src/cli/index.ts +420 -0
- package/microservices/microservice-domains/src/lib/brandsight.ts +285 -0
- package/microservices/microservice-domains/src/lib/godaddy.ts +328 -0
- package/microservices/microservice-domains/src/lib/namecheap.ts +474 -0
- package/microservices/microservice-domains/src/lib/registrar.ts +355 -0
- package/microservices/microservice-domains/src/mcp/index.ts +245 -0
- package/package.json +1 -1
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GoDaddy API integration for microservice-domains
|
|
3
|
+
*
|
|
4
|
+
* Environment variables:
|
|
5
|
+
* GODADDY_API_KEY — GoDaddy API key
|
|
6
|
+
* GODADDY_API_SECRET — GoDaddy API secret
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
CreateDomainInput,
|
|
11
|
+
UpdateDomainInput,
|
|
12
|
+
Domain,
|
|
13
|
+
} from "../db/domains.js";
|
|
14
|
+
|
|
15
|
+
// ============================================================
|
|
16
|
+
// Types
|
|
17
|
+
// ============================================================
|
|
18
|
+
|
|
19
|
+
export interface GoDaddyDomain {
|
|
20
|
+
domain: string;
|
|
21
|
+
status: string;
|
|
22
|
+
expires: string;
|
|
23
|
+
renewAuto: boolean;
|
|
24
|
+
nameServers: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface GoDaddyDomainDetail extends GoDaddyDomain {
|
|
28
|
+
domainId: number;
|
|
29
|
+
createdAt: string;
|
|
30
|
+
expirationProtected: boolean;
|
|
31
|
+
holdRegistrar: boolean;
|
|
32
|
+
locked: boolean;
|
|
33
|
+
privacy: boolean;
|
|
34
|
+
registrarCreatedAt: string;
|
|
35
|
+
renewDeadline: string;
|
|
36
|
+
transferProtected: boolean;
|
|
37
|
+
contactAdmin?: Record<string, unknown>;
|
|
38
|
+
contactBilling?: Record<string, unknown>;
|
|
39
|
+
contactRegistrant?: Record<string, unknown>;
|
|
40
|
+
contactTech?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface GoDaddyDnsRecord {
|
|
44
|
+
type: string;
|
|
45
|
+
name: string;
|
|
46
|
+
data: string;
|
|
47
|
+
ttl: number;
|
|
48
|
+
priority?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface GoDaddyAvailability {
|
|
52
|
+
available: boolean;
|
|
53
|
+
domain: string;
|
|
54
|
+
definitive: boolean;
|
|
55
|
+
price: number;
|
|
56
|
+
currency: string;
|
|
57
|
+
period: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface GoDaddySyncResult {
|
|
61
|
+
synced: number;
|
|
62
|
+
created: number;
|
|
63
|
+
updated: number;
|
|
64
|
+
errors: string[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export class GoDaddyApiError extends Error {
|
|
68
|
+
constructor(
|
|
69
|
+
message: string,
|
|
70
|
+
public statusCode: number,
|
|
71
|
+
public responseBody?: string
|
|
72
|
+
) {
|
|
73
|
+
super(message);
|
|
74
|
+
this.name = "GoDaddyApiError";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ============================================================
|
|
79
|
+
// Configuration
|
|
80
|
+
// ============================================================
|
|
81
|
+
|
|
82
|
+
const GODADDY_API_BASE = "https://api.godaddy.com";
|
|
83
|
+
|
|
84
|
+
function getCredentials(): { key: string; secret: string } {
|
|
85
|
+
const key = process.env["GODADDY_API_KEY"];
|
|
86
|
+
const secret = process.env["GODADDY_API_SECRET"];
|
|
87
|
+
|
|
88
|
+
if (!key || !secret) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
"GoDaddy API credentials not configured. Set GODADDY_API_KEY and GODADDY_API_SECRET environment variables."
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { key, secret };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getHeaders(): Record<string, string> {
|
|
98
|
+
const { key, secret } = getCredentials();
|
|
99
|
+
return {
|
|
100
|
+
Authorization: `sso-key ${key}:${secret}`,
|
|
101
|
+
"Content-Type": "application/json",
|
|
102
|
+
Accept: "application/json",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ============================================================
|
|
107
|
+
// Internal fetch helper (allows test injection)
|
|
108
|
+
// ============================================================
|
|
109
|
+
|
|
110
|
+
type FetchFn = typeof globalThis.fetch;
|
|
111
|
+
|
|
112
|
+
let _fetchFn: FetchFn = globalThis.fetch;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Override the fetch implementation (for testing).
|
|
116
|
+
* Pass `null` to restore the default.
|
|
117
|
+
*/
|
|
118
|
+
export function _setFetch(fn: FetchFn | null): void {
|
|
119
|
+
_fetchFn = fn ?? globalThis.fetch;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function apiRequest<T>(
|
|
123
|
+
method: string,
|
|
124
|
+
path: string,
|
|
125
|
+
body?: unknown
|
|
126
|
+
): Promise<T> {
|
|
127
|
+
const url = `${GODADDY_API_BASE}${path}`;
|
|
128
|
+
const headers = getHeaders();
|
|
129
|
+
|
|
130
|
+
const options: RequestInit = { method, headers };
|
|
131
|
+
if (body !== undefined) {
|
|
132
|
+
options.body = JSON.stringify(body);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const response = await _fetchFn(url, options);
|
|
136
|
+
|
|
137
|
+
if (!response.ok) {
|
|
138
|
+
const text = await response.text();
|
|
139
|
+
throw new GoDaddyApiError(
|
|
140
|
+
`GoDaddy API ${method} ${path} failed with status ${response.status}: ${text}`,
|
|
141
|
+
response.status,
|
|
142
|
+
text
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Some endpoints return 204 No Content
|
|
147
|
+
if (response.status === 204) {
|
|
148
|
+
return undefined as unknown as T;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return (await response.json()) as T;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ============================================================
|
|
155
|
+
// API Functions
|
|
156
|
+
// ============================================================
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* List all domains in the GoDaddy account.
|
|
160
|
+
*/
|
|
161
|
+
export async function listGoDaddyDomains(): Promise<GoDaddyDomain[]> {
|
|
162
|
+
return apiRequest<GoDaddyDomain[]>("GET", "/v1/domains");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get detailed info for a single domain.
|
|
167
|
+
*/
|
|
168
|
+
export async function getDomainInfo(
|
|
169
|
+
domain: string
|
|
170
|
+
): Promise<GoDaddyDomainDetail> {
|
|
171
|
+
return apiRequest<GoDaddyDomainDetail>(
|
|
172
|
+
"GET",
|
|
173
|
+
`/v1/domains/${encodeURIComponent(domain)}`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Renew a domain for 1 year.
|
|
179
|
+
*/
|
|
180
|
+
export async function renewDomain(
|
|
181
|
+
domain: string
|
|
182
|
+
): Promise<{ orderId: number; itemCount: number; total: number }> {
|
|
183
|
+
return apiRequest(
|
|
184
|
+
"POST",
|
|
185
|
+
`/v1/domains/${encodeURIComponent(domain)}/renew`,
|
|
186
|
+
{ period: 1 }
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get DNS records for a domain, optionally filtered by type.
|
|
192
|
+
*/
|
|
193
|
+
export async function getDnsRecords(
|
|
194
|
+
domain: string,
|
|
195
|
+
type?: string
|
|
196
|
+
): Promise<GoDaddyDnsRecord[]> {
|
|
197
|
+
const path = type
|
|
198
|
+
? `/v1/domains/${encodeURIComponent(domain)}/records/${encodeURIComponent(type)}`
|
|
199
|
+
: `/v1/domains/${encodeURIComponent(domain)}/records`;
|
|
200
|
+
return apiRequest<GoDaddyDnsRecord[]>("GET", path);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Replace all DNS records for a domain.
|
|
205
|
+
*/
|
|
206
|
+
export async function setDnsRecords(
|
|
207
|
+
domain: string,
|
|
208
|
+
records: GoDaddyDnsRecord[]
|
|
209
|
+
): Promise<void> {
|
|
210
|
+
await apiRequest<void>(
|
|
211
|
+
"PUT",
|
|
212
|
+
`/v1/domains/${encodeURIComponent(domain)}/records`,
|
|
213
|
+
records
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Check domain availability for purchase.
|
|
219
|
+
*/
|
|
220
|
+
export async function checkAvailability(
|
|
221
|
+
domain: string
|
|
222
|
+
): Promise<GoDaddyAvailability> {
|
|
223
|
+
return apiRequest<GoDaddyAvailability>(
|
|
224
|
+
"GET",
|
|
225
|
+
`/v1/domains/available?domain=${encodeURIComponent(domain)}`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ============================================================
|
|
230
|
+
// Sync to Local DB
|
|
231
|
+
// ============================================================
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Maps GoDaddy status strings to local domain statuses.
|
|
235
|
+
*/
|
|
236
|
+
function mapGoDaddyStatus(
|
|
237
|
+
gdStatus: string
|
|
238
|
+
): "active" | "expired" | "transferring" | "redemption" {
|
|
239
|
+
const s = gdStatus.toUpperCase();
|
|
240
|
+
if (s === "ACTIVE") return "active";
|
|
241
|
+
if (s === "EXPIRED") return "expired";
|
|
242
|
+
if (
|
|
243
|
+
s === "TRANSFERRED_OUT" ||
|
|
244
|
+
s === "TRANSFERRING" ||
|
|
245
|
+
s === "PENDING_TRANSFER"
|
|
246
|
+
)
|
|
247
|
+
return "transferring";
|
|
248
|
+
if (s === "REDEMPTION" || s === "PENDING_REDEMPTION") return "redemption";
|
|
249
|
+
return "active";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Sync all GoDaddy domains into the local database.
|
|
254
|
+
*
|
|
255
|
+
* Accepts DB helpers so the caller can inject the actual CRUD functions.
|
|
256
|
+
*/
|
|
257
|
+
export async function syncToLocalDb(dbFns: {
|
|
258
|
+
getDomainByName: (name: string) => Domain | null;
|
|
259
|
+
createDomain: (input: CreateDomainInput) => Domain;
|
|
260
|
+
updateDomain: (id: string, input: UpdateDomainInput) => Domain | null;
|
|
261
|
+
}): Promise<GoDaddySyncResult> {
|
|
262
|
+
const result: GoDaddySyncResult = {
|
|
263
|
+
synced: 0,
|
|
264
|
+
created: 0,
|
|
265
|
+
updated: 0,
|
|
266
|
+
errors: [],
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
let gdDomains: GoDaddyDomain[];
|
|
270
|
+
try {
|
|
271
|
+
gdDomains = await listGoDaddyDomains();
|
|
272
|
+
} catch (err) {
|
|
273
|
+
result.errors.push(
|
|
274
|
+
`Failed to list domains: ${err instanceof Error ? err.message : String(err)}`
|
|
275
|
+
);
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
for (const gd of gdDomains) {
|
|
280
|
+
try {
|
|
281
|
+
// Fetch full detail for each domain
|
|
282
|
+
let detail: GoDaddyDomainDetail;
|
|
283
|
+
try {
|
|
284
|
+
detail = await getDomainInfo(gd.domain);
|
|
285
|
+
} catch {
|
|
286
|
+
// Fall back to list-level data if detail fetch fails
|
|
287
|
+
detail = gd as GoDaddyDomainDetail;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const existing = dbFns.getDomainByName(gd.domain);
|
|
291
|
+
|
|
292
|
+
const domainData = {
|
|
293
|
+
name: gd.domain,
|
|
294
|
+
registrar: "GoDaddy",
|
|
295
|
+
status: mapGoDaddyStatus(gd.status),
|
|
296
|
+
expires_at: gd.expires
|
|
297
|
+
? new Date(gd.expires).toISOString()
|
|
298
|
+
: undefined,
|
|
299
|
+
auto_renew: gd.renewAuto,
|
|
300
|
+
nameservers: gd.nameServers || [],
|
|
301
|
+
registered_at: detail.createdAt
|
|
302
|
+
? new Date(detail.createdAt).toISOString()
|
|
303
|
+
: undefined,
|
|
304
|
+
metadata: {
|
|
305
|
+
godaddy_domain_id: (detail as GoDaddyDomainDetail).domainId,
|
|
306
|
+
provider: "godaddy",
|
|
307
|
+
locked: (detail as GoDaddyDomainDetail).locked,
|
|
308
|
+
privacy: (detail as GoDaddyDomainDetail).privacy,
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
if (existing) {
|
|
313
|
+
dbFns.updateDomain(existing.id, domainData);
|
|
314
|
+
result.updated++;
|
|
315
|
+
} else {
|
|
316
|
+
dbFns.createDomain(domainData);
|
|
317
|
+
result.created++;
|
|
318
|
+
}
|
|
319
|
+
result.synced++;
|
|
320
|
+
} catch (err) {
|
|
321
|
+
result.errors.push(
|
|
322
|
+
`Failed to sync ${gd.domain}: ${err instanceof Error ? err.message : String(err)}`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return result;
|
|
328
|
+
}
|