@hasna/microservices 0.0.4 → 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-ads/src/cli/index.ts +198 -0
- package/microservices/microservice-ads/src/db/campaigns.ts +304 -0
- package/microservices/microservice-ads/src/mcp/index.ts +160 -0
- 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-contracts/src/cli/index.ts +410 -23
- package/microservices/microservice-contracts/src/db/contracts.ts +430 -1
- package/microservices/microservice-contracts/src/db/migrations.ts +83 -0
- package/microservices/microservice-contracts/src/mcp/index.ts +312 -3
- package/microservices/microservice-domains/src/cli/index.ts +673 -0
- package/microservices/microservice-domains/src/db/domains.ts +613 -0
- package/microservices/microservice-domains/src/index.ts +21 -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 +413 -0
- package/microservices/microservice-hiring/src/cli/index.ts +318 -8
- package/microservices/microservice-hiring/src/db/hiring.ts +503 -0
- package/microservices/microservice-hiring/src/db/migrations.ts +21 -0
- package/microservices/microservice-hiring/src/index.ts +29 -0
- package/microservices/microservice-hiring/src/lib/scoring.ts +206 -0
- package/microservices/microservice-hiring/src/mcp/index.ts +245 -0
- package/microservices/microservice-payments/src/cli/index.ts +255 -3
- package/microservices/microservice-payments/src/db/migrations.ts +18 -0
- package/microservices/microservice-payments/src/db/payments.ts +552 -0
- package/microservices/microservice-payments/src/mcp/index.ts +223 -0
- package/microservices/microservice-payroll/src/cli/index.ts +269 -0
- package/microservices/microservice-payroll/src/db/migrations.ts +26 -0
- package/microservices/microservice-payroll/src/db/payroll.ts +636 -0
- package/microservices/microservice-payroll/src/mcp/index.ts +246 -0
- package/microservices/microservice-shipping/src/cli/index.ts +211 -3
- package/microservices/microservice-shipping/src/db/migrations.ts +8 -0
- package/microservices/microservice-shipping/src/db/shipping.ts +453 -3
- package/microservices/microservice-shipping/src/mcp/index.ts +149 -1
- package/microservices/microservice-social/src/cli/index.ts +244 -2
- package/microservices/microservice-social/src/db/migrations.ts +33 -0
- package/microservices/microservice-social/src/db/social.ts +378 -4
- package/microservices/microservice-social/src/mcp/index.ts +221 -1
- package/microservices/microservice-subscriptions/src/cli/index.ts +315 -0
- package/microservices/microservice-subscriptions/src/db/migrations.ts +68 -0
- package/microservices/microservice-subscriptions/src/db/subscriptions.ts +567 -3
- package/microservices/microservice-subscriptions/src/mcp/index.ts +267 -1
- package/package.json +1 -1
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brandsight API integration for brand monitoring and threat detection
|
|
3
|
+
*
|
|
4
|
+
* Requires environment variable:
|
|
5
|
+
* BRANDSIGHT_API_KEY — API key for Brandsight
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ============================================================
|
|
9
|
+
// Types
|
|
10
|
+
// ============================================================
|
|
11
|
+
|
|
12
|
+
export interface BrandsightAlert {
|
|
13
|
+
domain: string;
|
|
14
|
+
type: "typosquat" | "homoglyph" | "keyword" | "tld_variation";
|
|
15
|
+
registered_at: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface BrandMonitorResult {
|
|
19
|
+
brand: string;
|
|
20
|
+
alerts: BrandsightAlert[];
|
|
21
|
+
stub: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface WhoisHistoryEntry {
|
|
25
|
+
registrant: string;
|
|
26
|
+
date: string;
|
|
27
|
+
changes: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface WhoisHistoryResult {
|
|
31
|
+
domain: string;
|
|
32
|
+
history: WhoisHistoryEntry[];
|
|
33
|
+
stub: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ThreatAssessment {
|
|
37
|
+
domain: string;
|
|
38
|
+
risk_level: "low" | "medium" | "high" | "critical";
|
|
39
|
+
threats: string[];
|
|
40
|
+
recommendation: string;
|
|
41
|
+
stub: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class BrandsightApiError extends Error {
|
|
45
|
+
constructor(
|
|
46
|
+
message: string,
|
|
47
|
+
public statusCode?: number,
|
|
48
|
+
public responseBody?: string
|
|
49
|
+
) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.name = "BrandsightApiError";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ============================================================
|
|
56
|
+
// Configuration
|
|
57
|
+
// ============================================================
|
|
58
|
+
|
|
59
|
+
const API_BASE = "https://api.brandsight.com/v1";
|
|
60
|
+
|
|
61
|
+
export function getApiKey(): string {
|
|
62
|
+
const key = process.env["BRANDSIGHT_API_KEY"];
|
|
63
|
+
if (!key) {
|
|
64
|
+
throw new BrandsightApiError(
|
|
65
|
+
"BRANDSIGHT_API_KEY environment variable is not set"
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return key;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function getHeaders(apiKey: string): Record<string, string> {
|
|
72
|
+
return {
|
|
73
|
+
Authorization: `Bearer ${apiKey}`,
|
|
74
|
+
"Content-Type": "application/json",
|
|
75
|
+
Accept: "application/json",
|
|
76
|
+
"User-Agent": "microservice-domains/0.0.1",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ============================================================
|
|
81
|
+
// Internal fetch helper (allows test injection)
|
|
82
|
+
// ============================================================
|
|
83
|
+
|
|
84
|
+
type FetchFn = typeof globalThis.fetch;
|
|
85
|
+
|
|
86
|
+
let _fetchFn: FetchFn = globalThis.fetch;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Override the fetch implementation (for testing).
|
|
90
|
+
* Pass `null` to restore the default.
|
|
91
|
+
*/
|
|
92
|
+
export function _setFetch(fn: FetchFn | null): void {
|
|
93
|
+
_fetchFn = fn ?? globalThis.fetch;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function apiRequest<T>(path: string, apiKey: string): Promise<{ data: T; stub: false } | { data: null; stub: true }> {
|
|
97
|
+
const url = `${API_BASE}${path}`;
|
|
98
|
+
const headers = getHeaders(apiKey);
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const response = await _fetchFn(url, {
|
|
102
|
+
method: "GET",
|
|
103
|
+
headers,
|
|
104
|
+
signal: AbortSignal.timeout(15000),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
throw new BrandsightApiError(
|
|
109
|
+
`Brandsight API GET ${path} failed with status ${response.status}`,
|
|
110
|
+
response.status,
|
|
111
|
+
await response.text()
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const data = (await response.json()) as T;
|
|
116
|
+
return { data, stub: false };
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (error instanceof BrandsightApiError) throw error;
|
|
119
|
+
// API unreachable — return stub indicator
|
|
120
|
+
return { data: null, stub: true };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ============================================================
|
|
125
|
+
// Stub Data Generators
|
|
126
|
+
// ============================================================
|
|
127
|
+
|
|
128
|
+
function generateStubAlerts(brandName: string): BrandsightAlert[] {
|
|
129
|
+
const now = new Date().toISOString();
|
|
130
|
+
return [
|
|
131
|
+
{
|
|
132
|
+
domain: `${brandName}-deals.com`,
|
|
133
|
+
type: "keyword",
|
|
134
|
+
registered_at: now,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
domain: `${brandName.replace(/a/gi, "4").replace(/e/gi, "3")}.com`,
|
|
138
|
+
type: "homoglyph",
|
|
139
|
+
registered_at: now,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
domain: `${brandName}s.com`,
|
|
143
|
+
type: "typosquat",
|
|
144
|
+
registered_at: now,
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function generateStubSimilarDomains(domain: string): string[] {
|
|
150
|
+
const base = domain.replace(/\.[^.]+$/, "");
|
|
151
|
+
const tld = domain.slice(base.length);
|
|
152
|
+
return [
|
|
153
|
+
`${base}-online${tld}`,
|
|
154
|
+
`${base}s${tld}`,
|
|
155
|
+
`${base.replace(/a/gi, "4")}${tld}`,
|
|
156
|
+
`${base}-app${tld}`,
|
|
157
|
+
`get${base}${tld}`,
|
|
158
|
+
];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function generateStubWhoisHistory(domain: string): WhoisHistoryEntry[] {
|
|
162
|
+
return [
|
|
163
|
+
{
|
|
164
|
+
registrant: "Privacy Proxy Service",
|
|
165
|
+
date: "2023-01-15T00:00:00Z",
|
|
166
|
+
changes: ["registrant_changed", "nameserver_changed"],
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
registrant: "Original Owner LLC",
|
|
170
|
+
date: "2020-06-01T00:00:00Z",
|
|
171
|
+
changes: ["initial_registration"],
|
|
172
|
+
},
|
|
173
|
+
];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function generateStubThreatAssessment(domain: string): Omit<ThreatAssessment, "stub"> {
|
|
177
|
+
return {
|
|
178
|
+
domain,
|
|
179
|
+
risk_level: "low",
|
|
180
|
+
threats: [],
|
|
181
|
+
recommendation: "No immediate threats detected. Continue routine monitoring.",
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ============================================================
|
|
186
|
+
// API Functions
|
|
187
|
+
// ============================================================
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Monitor a brand name for new domain registrations that are similar.
|
|
191
|
+
*/
|
|
192
|
+
export async function monitorBrand(brandName: string): Promise<BrandMonitorResult> {
|
|
193
|
+
const apiKey = getApiKey();
|
|
194
|
+
const result = await apiRequest<{ alerts: BrandsightAlert[] }>(
|
|
195
|
+
`/brands/${encodeURIComponent(brandName)}/monitor`,
|
|
196
|
+
apiKey
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
if (result.stub) {
|
|
200
|
+
return {
|
|
201
|
+
brand: brandName,
|
|
202
|
+
alerts: generateStubAlerts(brandName),
|
|
203
|
+
stub: true,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
brand: brandName,
|
|
209
|
+
alerts: result.data!.alerts,
|
|
210
|
+
stub: false,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Find typosquat/competing domains similar to the given domain.
|
|
216
|
+
*/
|
|
217
|
+
export async function getSimilarDomains(domain: string): Promise<{ domain: string; similar: string[]; stub: boolean }> {
|
|
218
|
+
const apiKey = getApiKey();
|
|
219
|
+
const result = await apiRequest<{ similar: string[] }>(
|
|
220
|
+
`/domains/${encodeURIComponent(domain)}/similar`,
|
|
221
|
+
apiKey
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
if (result.stub) {
|
|
225
|
+
return {
|
|
226
|
+
domain,
|
|
227
|
+
similar: generateStubSimilarDomains(domain),
|
|
228
|
+
stub: true,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
domain,
|
|
234
|
+
similar: result.data!.similar,
|
|
235
|
+
stub: false,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get historical WHOIS records for a domain.
|
|
241
|
+
*/
|
|
242
|
+
export async function getWhoisHistory(domain: string): Promise<WhoisHistoryResult> {
|
|
243
|
+
const apiKey = getApiKey();
|
|
244
|
+
const result = await apiRequest<{ history: WhoisHistoryEntry[] }>(
|
|
245
|
+
`/domains/${encodeURIComponent(domain)}/whois-history`,
|
|
246
|
+
apiKey
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
if (result.stub) {
|
|
250
|
+
return {
|
|
251
|
+
domain,
|
|
252
|
+
history: generateStubWhoisHistory(domain),
|
|
253
|
+
stub: true,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
domain,
|
|
259
|
+
history: result.data!.history,
|
|
260
|
+
stub: false,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Get a threat assessment for a domain.
|
|
266
|
+
*/
|
|
267
|
+
export async function getThreatAssessment(domain: string): Promise<ThreatAssessment> {
|
|
268
|
+
const apiKey = getApiKey();
|
|
269
|
+
const result = await apiRequest<Omit<ThreatAssessment, "stub">>(
|
|
270
|
+
`/domains/${encodeURIComponent(domain)}/threats`,
|
|
271
|
+
apiKey
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
if (result.stub) {
|
|
275
|
+
return {
|
|
276
|
+
...generateStubThreatAssessment(domain),
|
|
277
|
+
stub: true,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
...result.data!,
|
|
283
|
+
stub: false,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
@@ -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
|
+
}
|