@hasna/microservices 0.0.5 → 0.0.7
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 +350 -0
- package/microservices/microservice-domains/src/lib/godaddy.ts +338 -0
- package/microservices/microservice-domains/src/lib/namecheap.ts +262 -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,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Namecheap API integration for domain management
|
|
3
|
+
*
|
|
4
|
+
* Uses the Connector SDK pattern from connect-namecheap.
|
|
5
|
+
* When @hasna/connect-namecheap is published to npm, replace the
|
|
6
|
+
* relative connector import with the package import.
|
|
7
|
+
*
|
|
8
|
+
* Requires environment variables:
|
|
9
|
+
* NAMECHEAP_API_KEY — API key from Namecheap
|
|
10
|
+
* NAMECHEAP_USERNAME — Namecheap account username
|
|
11
|
+
* NAMECHEAP_CLIENT_IP — Whitelisted client IP address
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Re-export types used by consumers (CLI, MCP, registrar)
|
|
15
|
+
export type {
|
|
16
|
+
ConnectorConfig as NamecheapConfig,
|
|
17
|
+
Domain as NamecheapDomain,
|
|
18
|
+
DomainInfo as NamecheapDomainInfo,
|
|
19
|
+
DnsRecord as NamecheapDnsRecord,
|
|
20
|
+
AvailabilityResult as NamecheapAvailability,
|
|
21
|
+
RenewResult as NamecheapRenewResult,
|
|
22
|
+
} from "../../../../../open-connectors/connectors/connect-namecheap/src/index.js";
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
Connector,
|
|
26
|
+
type ConnectorConfig,
|
|
27
|
+
type Domain as NamecheapDomain,
|
|
28
|
+
type DomainInfo as NamecheapDomainInfo,
|
|
29
|
+
type DnsRecord as NamecheapDnsRecord,
|
|
30
|
+
} from "../../../../../open-connectors/connectors/connect-namecheap/src/index.js";
|
|
31
|
+
|
|
32
|
+
// ============================================================
|
|
33
|
+
// Sync Result Type (microservice-specific, not in connector)
|
|
34
|
+
// ============================================================
|
|
35
|
+
|
|
36
|
+
export interface NamecheapSyncResult {
|
|
37
|
+
synced: number;
|
|
38
|
+
errors: string[];
|
|
39
|
+
domains: string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ============================================================
|
|
43
|
+
// Connector Instance Management
|
|
44
|
+
// ============================================================
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create a Namecheap connector from environment variables.
|
|
48
|
+
* Wraps Connector.fromEnv() with the same env var validation.
|
|
49
|
+
*/
|
|
50
|
+
export function getConfig(): ConnectorConfig {
|
|
51
|
+
const apiKey = process.env["NAMECHEAP_API_KEY"];
|
|
52
|
+
const username = process.env["NAMECHEAP_USERNAME"];
|
|
53
|
+
const clientIp = process.env["NAMECHEAP_CLIENT_IP"];
|
|
54
|
+
|
|
55
|
+
if (!apiKey) throw new Error("NAMECHEAP_API_KEY environment variable is not set");
|
|
56
|
+
if (!username) throw new Error("NAMECHEAP_USERNAME environment variable is not set");
|
|
57
|
+
if (!clientIp) throw new Error("NAMECHEAP_CLIENT_IP environment variable is not set");
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
apiKey,
|
|
61
|
+
username,
|
|
62
|
+
clientIp,
|
|
63
|
+
sandbox: process.env["NAMECHEAP_SANDBOX"] === "true",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function createConnector(config?: ConnectorConfig): Connector {
|
|
68
|
+
return new Connector(config || getConfig());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ============================================================
|
|
72
|
+
// API Functions (thin wrappers around connector)
|
|
73
|
+
// ============================================================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* List all domains in the Namecheap account
|
|
77
|
+
*/
|
|
78
|
+
export async function listNamecheapDomains(config?: ConnectorConfig): Promise<NamecheapDomain[]> {
|
|
79
|
+
const connector = createConnector(config);
|
|
80
|
+
return connector.domains.list();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get detailed info for a specific domain
|
|
85
|
+
*/
|
|
86
|
+
export async function getDomainInfo(domain: string, config?: ConnectorConfig): Promise<NamecheapDomainInfo> {
|
|
87
|
+
const connector = createConnector(config);
|
|
88
|
+
return connector.domains.getInfo(domain);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Renew a domain
|
|
93
|
+
*/
|
|
94
|
+
export async function renewDomain(domain: string, years: number = 1, config?: ConnectorConfig) {
|
|
95
|
+
const connector = createConnector(config);
|
|
96
|
+
return connector.domains.renew(domain, years);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get DNS host records for a domain
|
|
101
|
+
*/
|
|
102
|
+
export async function getDnsRecords(domain: string, sld: string, tld: string, config?: ConnectorConfig): Promise<NamecheapDnsRecord[]> {
|
|
103
|
+
const connector = createConnector(config);
|
|
104
|
+
return connector.dns.getHosts(sld, tld);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Set DNS host records for a domain
|
|
109
|
+
*/
|
|
110
|
+
export async function setDnsRecords(
|
|
111
|
+
domain: string,
|
|
112
|
+
sld: string,
|
|
113
|
+
tld: string,
|
|
114
|
+
records: NamecheapDnsRecord[],
|
|
115
|
+
config?: ConnectorConfig
|
|
116
|
+
): Promise<boolean> {
|
|
117
|
+
const connector = createConnector(config);
|
|
118
|
+
return connector.dns.setHosts(sld, tld, records);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Check domain availability
|
|
123
|
+
*/
|
|
124
|
+
export async function checkAvailability(domain: string, config?: ConnectorConfig) {
|
|
125
|
+
const connector = createConnector(config);
|
|
126
|
+
return connector.domains.check(domain);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ============================================================
|
|
130
|
+
// Domain Helpers (microservice-specific)
|
|
131
|
+
// ============================================================
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Split a domain name into SLD (second-level domain) and TLD (top-level domain)
|
|
135
|
+
*/
|
|
136
|
+
export function splitDomain(domain: string): { sld: string; tld: string } {
|
|
137
|
+
const parts = domain.split(".");
|
|
138
|
+
if (parts.length < 2) {
|
|
139
|
+
throw new Error(`Invalid domain: ${domain}`);
|
|
140
|
+
}
|
|
141
|
+
// Handle multi-part TLDs like .co.uk
|
|
142
|
+
if (parts.length >= 3 && ["co", "com", "org", "net", "ac", "gov"].includes(parts[parts.length - 2])) {
|
|
143
|
+
return {
|
|
144
|
+
sld: parts.slice(0, -2).join("."),
|
|
145
|
+
tld: parts.slice(-2).join("."),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
sld: parts.slice(0, -1).join("."),
|
|
150
|
+
tld: parts[parts.length - 1],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ============================================================
|
|
155
|
+
// Sync to Local DB (microservice business logic)
|
|
156
|
+
// ============================================================
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Sync domains from Namecheap to local database
|
|
160
|
+
* Calls listDomains + getDomainInfo for each, upserts into local domains table
|
|
161
|
+
*/
|
|
162
|
+
export async function syncToLocalDb(dbFunctions: {
|
|
163
|
+
getDomainByName: (name: string) => { id: string } | null;
|
|
164
|
+
createDomain: (input: {
|
|
165
|
+
name: string;
|
|
166
|
+
registrar?: string;
|
|
167
|
+
status?: string;
|
|
168
|
+
registered_at?: string;
|
|
169
|
+
expires_at?: string;
|
|
170
|
+
auto_renew?: boolean;
|
|
171
|
+
nameservers?: string[];
|
|
172
|
+
}) => { id: string; name: string };
|
|
173
|
+
updateDomain: (
|
|
174
|
+
id: string,
|
|
175
|
+
input: {
|
|
176
|
+
registrar?: string;
|
|
177
|
+
status?: string;
|
|
178
|
+
registered_at?: string;
|
|
179
|
+
expires_at?: string;
|
|
180
|
+
auto_renew?: boolean;
|
|
181
|
+
nameservers?: string[];
|
|
182
|
+
}
|
|
183
|
+
) => unknown;
|
|
184
|
+
}, config?: ConnectorConfig): Promise<NamecheapSyncResult> {
|
|
185
|
+
const connector = createConnector(config);
|
|
186
|
+
const result: NamecheapSyncResult = { synced: 0, errors: [], domains: [] };
|
|
187
|
+
|
|
188
|
+
let ncDomains: NamecheapDomain[];
|
|
189
|
+
try {
|
|
190
|
+
ncDomains = await connector.domains.list();
|
|
191
|
+
} catch (error) {
|
|
192
|
+
throw new Error(`Failed to list Namecheap domains: ${error instanceof Error ? error.message : String(error)}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (const ncDomain of ncDomains) {
|
|
196
|
+
try {
|
|
197
|
+
// Get detailed info via connector
|
|
198
|
+
let info: NamecheapDomainInfo;
|
|
199
|
+
try {
|
|
200
|
+
info = await connector.domains.getInfo(ncDomain.domain);
|
|
201
|
+
} catch {
|
|
202
|
+
// Fall back to basic info if getInfo fails
|
|
203
|
+
info = {
|
|
204
|
+
domain: ncDomain.domain,
|
|
205
|
+
registrar: "Namecheap",
|
|
206
|
+
created: "",
|
|
207
|
+
expires: ncDomain.expiry,
|
|
208
|
+
nameservers: [],
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Normalize dates to ISO format
|
|
213
|
+
const expiresAt = normalizeDate(info.expires || ncDomain.expiry);
|
|
214
|
+
const createdAt = normalizeDate(info.created);
|
|
215
|
+
|
|
216
|
+
// Upsert into local DB
|
|
217
|
+
const existing = dbFunctions.getDomainByName(ncDomain.domain);
|
|
218
|
+
if (existing) {
|
|
219
|
+
dbFunctions.updateDomain(existing.id, {
|
|
220
|
+
registrar: "Namecheap",
|
|
221
|
+
status: "active",
|
|
222
|
+
registered_at: createdAt || undefined,
|
|
223
|
+
expires_at: expiresAt || undefined,
|
|
224
|
+
auto_renew: ncDomain.autoRenew,
|
|
225
|
+
nameservers: info.nameservers.length > 0 ? info.nameservers : undefined,
|
|
226
|
+
});
|
|
227
|
+
} else {
|
|
228
|
+
dbFunctions.createDomain({
|
|
229
|
+
name: ncDomain.domain,
|
|
230
|
+
registrar: "Namecheap",
|
|
231
|
+
status: "active",
|
|
232
|
+
registered_at: createdAt || undefined,
|
|
233
|
+
expires_at: expiresAt || undefined,
|
|
234
|
+
auto_renew: ncDomain.autoRenew,
|
|
235
|
+
nameservers: info.nameservers,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
result.synced++;
|
|
240
|
+
result.domains.push(ncDomain.domain);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
result.errors.push(`${ncDomain.domain}: ${error instanceof Error ? error.message : String(error)}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ============================================================
|
|
250
|
+
// Helpers
|
|
251
|
+
// ============================================================
|
|
252
|
+
|
|
253
|
+
function normalizeDate(dateStr: string): string | null {
|
|
254
|
+
if (!dateStr) return null;
|
|
255
|
+
try {
|
|
256
|
+
const d = new Date(dateStr);
|
|
257
|
+
if (isNaN(d.getTime())) return null;
|
|
258
|
+
return d.toISOString();
|
|
259
|
+
} catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified registrar provider system
|
|
3
|
+
*
|
|
4
|
+
* Wraps Namecheap, GoDaddy, and Brandsight into a common RegistrarProvider interface.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Domain, CreateDomainInput, UpdateDomainInput } from "../db/domains.js";
|
|
8
|
+
import * as namecheap from "./namecheap.js";
|
|
9
|
+
import * as godaddy from "./godaddy.js";
|
|
10
|
+
|
|
11
|
+
// ============================================================
|
|
12
|
+
// Types
|
|
13
|
+
// ============================================================
|
|
14
|
+
|
|
15
|
+
export interface ProviderDnsRecord {
|
|
16
|
+
type: string;
|
|
17
|
+
name: string;
|
|
18
|
+
value: string;
|
|
19
|
+
ttl: number;
|
|
20
|
+
priority?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ProviderDomainInfo {
|
|
24
|
+
domain: string;
|
|
25
|
+
registrar: string;
|
|
26
|
+
created: string;
|
|
27
|
+
expires: string;
|
|
28
|
+
nameservers: string[];
|
|
29
|
+
status: string;
|
|
30
|
+
auto_renew: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ProviderRenewResult {
|
|
34
|
+
domain: string;
|
|
35
|
+
success: boolean;
|
|
36
|
+
orderId?: string;
|
|
37
|
+
chargedAmount?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ProviderSyncResult {
|
|
41
|
+
synced: number;
|
|
42
|
+
created: number;
|
|
43
|
+
updated: number;
|
|
44
|
+
errors: string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ProviderAvailability {
|
|
48
|
+
domain: string;
|
|
49
|
+
available: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type DbFunctions = {
|
|
53
|
+
getDomainByName: (name: string) => Domain | null;
|
|
54
|
+
createDomain: (input: CreateDomainInput) => Domain;
|
|
55
|
+
updateDomain: (id: string, input: UpdateDomainInput) => Domain | null;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export interface RegistrarProvider {
|
|
59
|
+
name: string;
|
|
60
|
+
listDomains(): Promise<ProviderDomainInfo[]>;
|
|
61
|
+
getDomainInfo(domain: string): Promise<ProviderDomainInfo>;
|
|
62
|
+
renewDomain(domain: string): Promise<ProviderRenewResult>;
|
|
63
|
+
getDnsRecords(domain: string): Promise<ProviderDnsRecord[]>;
|
|
64
|
+
setDnsRecords(domain: string, records: ProviderDnsRecord[]): Promise<boolean>;
|
|
65
|
+
checkAvailability(domain: string): Promise<ProviderAvailability>;
|
|
66
|
+
syncToLocalDb(dbFns: DbFunctions): Promise<ProviderSyncResult>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ProviderInfo {
|
|
70
|
+
name: string;
|
|
71
|
+
configured: boolean;
|
|
72
|
+
envVars: string[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface SyncAllResult {
|
|
76
|
+
providers: { name: string; result: ProviderSyncResult }[];
|
|
77
|
+
totalSynced: number;
|
|
78
|
+
totalErrors: string[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ============================================================
|
|
82
|
+
// Namecheap Provider Adapter
|
|
83
|
+
// ============================================================
|
|
84
|
+
|
|
85
|
+
function createNamecheapProvider(): RegistrarProvider {
|
|
86
|
+
return {
|
|
87
|
+
name: "namecheap",
|
|
88
|
+
|
|
89
|
+
async listDomains(): Promise<ProviderDomainInfo[]> {
|
|
90
|
+
const config = namecheap.getConfig();
|
|
91
|
+
const domains = await namecheap.listNamecheapDomains(config);
|
|
92
|
+
return domains.map((d) => ({
|
|
93
|
+
domain: d.domain,
|
|
94
|
+
registrar: "Namecheap",
|
|
95
|
+
created: "",
|
|
96
|
+
expires: d.expiry,
|
|
97
|
+
nameservers: [],
|
|
98
|
+
status: "active",
|
|
99
|
+
auto_renew: d.autoRenew,
|
|
100
|
+
}));
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
async getDomainInfo(domain: string): Promise<ProviderDomainInfo> {
|
|
104
|
+
const config = namecheap.getConfig();
|
|
105
|
+
const info = await namecheap.getDomainInfo(domain, config);
|
|
106
|
+
return {
|
|
107
|
+
domain: info.domain,
|
|
108
|
+
registrar: info.registrar,
|
|
109
|
+
created: info.created,
|
|
110
|
+
expires: info.expires,
|
|
111
|
+
nameservers: info.nameservers,
|
|
112
|
+
status: "active",
|
|
113
|
+
auto_renew: true,
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
async renewDomain(domain: string): Promise<ProviderRenewResult> {
|
|
118
|
+
const config = namecheap.getConfig();
|
|
119
|
+
const result = await namecheap.renewDomain(domain, 1, config);
|
|
120
|
+
return {
|
|
121
|
+
domain: result.domain,
|
|
122
|
+
success: result.success,
|
|
123
|
+
orderId: result.orderId,
|
|
124
|
+
chargedAmount: result.chargedAmount,
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
async getDnsRecords(domain: string): Promise<ProviderDnsRecord[]> {
|
|
129
|
+
const config = namecheap.getConfig();
|
|
130
|
+
const { sld, tld } = namecheap.splitDomain(domain);
|
|
131
|
+
const records = await namecheap.getDnsRecords(domain, sld, tld, config);
|
|
132
|
+
return records.map((r) => ({
|
|
133
|
+
type: r.type,
|
|
134
|
+
name: r.name,
|
|
135
|
+
value: r.address,
|
|
136
|
+
ttl: r.ttl,
|
|
137
|
+
priority: r.mxPref,
|
|
138
|
+
}));
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
async setDnsRecords(domain: string, records: ProviderDnsRecord[]): Promise<boolean> {
|
|
142
|
+
const config = namecheap.getConfig();
|
|
143
|
+
const { sld, tld } = namecheap.splitDomain(domain);
|
|
144
|
+
const ncRecords = records.map((r) => ({
|
|
145
|
+
type: r.type,
|
|
146
|
+
name: r.name,
|
|
147
|
+
address: r.value,
|
|
148
|
+
ttl: r.ttl,
|
|
149
|
+
mxPref: r.priority,
|
|
150
|
+
}));
|
|
151
|
+
return namecheap.setDnsRecords(domain, sld, tld, ncRecords, config);
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
async checkAvailability(domain: string): Promise<ProviderAvailability> {
|
|
155
|
+
const config = namecheap.getConfig();
|
|
156
|
+
const result = await namecheap.checkAvailability(domain, config);
|
|
157
|
+
return { domain: result.domain, available: result.available };
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
async syncToLocalDb(dbFns: DbFunctions): Promise<ProviderSyncResult> {
|
|
161
|
+
const result = await namecheap.syncToLocalDb(dbFns);
|
|
162
|
+
return {
|
|
163
|
+
synced: result.synced,
|
|
164
|
+
created: 0,
|
|
165
|
+
updated: 0,
|
|
166
|
+
errors: result.errors,
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ============================================================
|
|
173
|
+
// GoDaddy Provider Adapter
|
|
174
|
+
// ============================================================
|
|
175
|
+
|
|
176
|
+
function createGoDaddyProvider(): RegistrarProvider {
|
|
177
|
+
return {
|
|
178
|
+
name: "godaddy",
|
|
179
|
+
|
|
180
|
+
async listDomains(): Promise<ProviderDomainInfo[]> {
|
|
181
|
+
const domains = await godaddy.listGoDaddyDomains();
|
|
182
|
+
return domains.map((d) => ({
|
|
183
|
+
domain: d.domain,
|
|
184
|
+
registrar: "GoDaddy",
|
|
185
|
+
created: "",
|
|
186
|
+
expires: d.expires,
|
|
187
|
+
nameservers: d.nameServers || [],
|
|
188
|
+
status: d.status.toLowerCase(),
|
|
189
|
+
auto_renew: d.renewAuto,
|
|
190
|
+
}));
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
async getDomainInfo(domain: string): Promise<ProviderDomainInfo> {
|
|
194
|
+
const detail = await godaddy.getDomainInfo(domain);
|
|
195
|
+
return {
|
|
196
|
+
domain: detail.domain,
|
|
197
|
+
registrar: "GoDaddy",
|
|
198
|
+
created: detail.createdAt || "",
|
|
199
|
+
expires: detail.expires,
|
|
200
|
+
nameservers: detail.nameServers || [],
|
|
201
|
+
status: detail.status.toLowerCase(),
|
|
202
|
+
auto_renew: detail.renewAuto,
|
|
203
|
+
};
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
async renewDomain(domain: string): Promise<ProviderRenewResult> {
|
|
207
|
+
const result = await godaddy.renewDomain(domain);
|
|
208
|
+
return {
|
|
209
|
+
domain,
|
|
210
|
+
success: true,
|
|
211
|
+
orderId: String(result.orderId),
|
|
212
|
+
chargedAmount: String(result.total),
|
|
213
|
+
};
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
async getDnsRecords(domain: string): Promise<ProviderDnsRecord[]> {
|
|
217
|
+
const records = await godaddy.getDnsRecords(domain);
|
|
218
|
+
return records.map((r) => ({
|
|
219
|
+
type: r.type,
|
|
220
|
+
name: r.name,
|
|
221
|
+
value: r.data,
|
|
222
|
+
ttl: r.ttl,
|
|
223
|
+
priority: r.priority,
|
|
224
|
+
}));
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
async setDnsRecords(domain: string, records: ProviderDnsRecord[]): Promise<boolean> {
|
|
228
|
+
const gdRecords = records.map((r) => ({
|
|
229
|
+
type: r.type,
|
|
230
|
+
name: r.name,
|
|
231
|
+
data: r.value,
|
|
232
|
+
ttl: r.ttl,
|
|
233
|
+
priority: r.priority,
|
|
234
|
+
}));
|
|
235
|
+
await godaddy.setDnsRecords(domain, gdRecords);
|
|
236
|
+
return true;
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
async checkAvailability(domain: string): Promise<ProviderAvailability> {
|
|
240
|
+
const result = await godaddy.checkAvailability(domain);
|
|
241
|
+
return { domain: result.domain, available: result.available };
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
async syncToLocalDb(dbFns: DbFunctions): Promise<ProviderSyncResult> {
|
|
245
|
+
const result = await godaddy.syncToLocalDb(dbFns);
|
|
246
|
+
return {
|
|
247
|
+
synced: result.synced,
|
|
248
|
+
created: result.created,
|
|
249
|
+
updated: result.updated,
|
|
250
|
+
errors: result.errors,
|
|
251
|
+
};
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ============================================================
|
|
257
|
+
// Provider Factory
|
|
258
|
+
// ============================================================
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Get a unified RegistrarProvider by name.
|
|
262
|
+
*/
|
|
263
|
+
export function getProvider(name: "namecheap" | "godaddy"): RegistrarProvider {
|
|
264
|
+
switch (name) {
|
|
265
|
+
case "namecheap":
|
|
266
|
+
return createNamecheapProvider();
|
|
267
|
+
case "godaddy":
|
|
268
|
+
return createGoDaddyProvider();
|
|
269
|
+
default:
|
|
270
|
+
throw new Error(`Unknown registrar provider: ${name}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Check which providers have their API keys configured.
|
|
276
|
+
*/
|
|
277
|
+
export function getAvailableProviders(): ProviderInfo[] {
|
|
278
|
+
const providers: ProviderInfo[] = [
|
|
279
|
+
{
|
|
280
|
+
name: "namecheap",
|
|
281
|
+
configured: !!(
|
|
282
|
+
process.env["NAMECHEAP_API_KEY"] &&
|
|
283
|
+
process.env["NAMECHEAP_USERNAME"] &&
|
|
284
|
+
process.env["NAMECHEAP_CLIENT_IP"]
|
|
285
|
+
),
|
|
286
|
+
envVars: ["NAMECHEAP_API_KEY", "NAMECHEAP_USERNAME", "NAMECHEAP_CLIENT_IP"],
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: "godaddy",
|
|
290
|
+
configured: !!(
|
|
291
|
+
process.env["GODADDY_API_KEY"] &&
|
|
292
|
+
process.env["GODADDY_API_SECRET"]
|
|
293
|
+
),
|
|
294
|
+
envVars: ["GODADDY_API_KEY", "GODADDY_API_SECRET"],
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
name: "brandsight",
|
|
298
|
+
configured: !!process.env["BRANDSIGHT_API_KEY"],
|
|
299
|
+
envVars: ["BRANDSIGHT_API_KEY"],
|
|
300
|
+
},
|
|
301
|
+
];
|
|
302
|
+
|
|
303
|
+
return providers;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Sync domains from ALL configured registrar providers sequentially.
|
|
308
|
+
*/
|
|
309
|
+
export async function syncAll(dbFns: DbFunctions): Promise<SyncAllResult> {
|
|
310
|
+
const available = getAvailableProviders().filter(
|
|
311
|
+
(p) => p.configured && (p.name === "namecheap" || p.name === "godaddy")
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const result: SyncAllResult = {
|
|
315
|
+
providers: [],
|
|
316
|
+
totalSynced: 0,
|
|
317
|
+
totalErrors: [],
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
for (const info of available) {
|
|
321
|
+
try {
|
|
322
|
+
const provider = getProvider(info.name as "namecheap" | "godaddy");
|
|
323
|
+
const syncResult = await provider.syncToLocalDb(dbFns);
|
|
324
|
+
result.providers.push({ name: info.name, result: syncResult });
|
|
325
|
+
result.totalSynced += syncResult.synced;
|
|
326
|
+
result.totalErrors.push(...syncResult.errors.map((e) => `[${info.name}] ${e}`));
|
|
327
|
+
} catch (error) {
|
|
328
|
+
const msg = `[${info.name}] Sync failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
329
|
+
result.totalErrors.push(msg);
|
|
330
|
+
result.providers.push({
|
|
331
|
+
name: info.name,
|
|
332
|
+
result: { synced: 0, created: 0, updated: 0, errors: [msg] },
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Auto-detect which registrar provider a domain uses based on its DB record.
|
|
342
|
+
* Returns the provider name or null if not determinable.
|
|
343
|
+
*/
|
|
344
|
+
export function autoDetectRegistrar(
|
|
345
|
+
domain: string,
|
|
346
|
+
getDomainByName: (name: string) => Domain | null
|
|
347
|
+
): "namecheap" | "godaddy" | null {
|
|
348
|
+
const dbDomain = getDomainByName(domain);
|
|
349
|
+
if (!dbDomain || !dbDomain.registrar) return null;
|
|
350
|
+
|
|
351
|
+
const registrar = dbDomain.registrar.toLowerCase();
|
|
352
|
+
if (registrar.includes("namecheap")) return "namecheap";
|
|
353
|
+
if (registrar.includes("godaddy")) return "godaddy";
|
|
354
|
+
return null;
|
|
355
|
+
}
|