insta 0.0.76 → 0.0.77
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/commands/domain.js +58 -8
- package/dist/index.js +15 -0
- package/package.json +1 -1
package/dist/commands/domain.js
CHANGED
|
@@ -34,15 +34,8 @@ export async function domainSearch(keyword, opts, deps) {
|
|
|
34
34
|
info(`buy one: insta domain buy ${buyable.domainName}`);
|
|
35
35
|
}
|
|
36
36
|
export async function domainBuy(name, opts, deps) {
|
|
37
|
+
const years = whole('--years', opts.years);
|
|
37
38
|
const { api, project: p } = await domainDeps(deps);
|
|
38
|
-
// JSON.stringify drops undefined but keeps NaN as null, which the platform rejects as a type
|
|
39
|
-
// error rather than a bad term — so a malformed --years is refused here, with the reason.
|
|
40
|
-
let years;
|
|
41
|
-
if (opts.years !== undefined) {
|
|
42
|
-
years = Number(opts.years);
|
|
43
|
-
if (!Number.isInteger(years))
|
|
44
|
-
die(`--years must be a whole number of years, not ${opts.years}`);
|
|
45
|
-
}
|
|
46
39
|
const res = await api.rawRequest('POST', `/projects/${p.projectId}/domains/orders`, { domainName: name, years });
|
|
47
40
|
if (handleApproval(res, opts.json))
|
|
48
41
|
return;
|
|
@@ -138,4 +131,61 @@ export async function domainStatus(name, opts, deps) {
|
|
|
138
131
|
for (const line of domain ? domainLines(domain) : orderStatusLines(order))
|
|
139
132
|
info(line);
|
|
140
133
|
}
|
|
134
|
+
export function recordLines(records) {
|
|
135
|
+
const w = (pick) => Math.max(...records.map((r) => pick(r).length));
|
|
136
|
+
const idW = w((r) => String(r.id)), typeW = w((r) => r.type), nameW = w((r) => r.fqdn), answerW = w((r) => r.answer), ttlW = w((r) => String(r.ttl));
|
|
137
|
+
return records.map((r) => {
|
|
138
|
+
const line = ` ${String(r.id).padEnd(idW)} ${r.type.padEnd(typeW)} ${r.fqdn.padEnd(nameW)} ${r.answer.padEnd(answerW)} ${String(r.ttl).padEnd(ttlW)} ${r.priority === undefined ? '' : String(r.priority)}`.trimEnd();
|
|
139
|
+
if (r.managed)
|
|
140
|
+
return `${line} (managed${r.hostname ? ` — published for ${r.hostname}` : ' — no hostname claims it; remove drops it'})`;
|
|
141
|
+
return line;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
const recordPath = (orgId, domainName) => `/orgs/${orgId}/domains/${encodeURIComponent(domainName)}/records`;
|
|
145
|
+
// Number() reads "" as 0 and "1e3" as 1000, and keeps NaN, which the platform refuses as a type error naming no flag.
|
|
146
|
+
function whole(flag, value) {
|
|
147
|
+
if (value === undefined)
|
|
148
|
+
return undefined;
|
|
149
|
+
const digits = value.trim();
|
|
150
|
+
if (!/^\d+$/.test(digits))
|
|
151
|
+
die(`${flag} must be a whole number, not ${JSON.stringify(value)}`);
|
|
152
|
+
return Number(digits);
|
|
153
|
+
}
|
|
154
|
+
export async function domainRecordsList(domainName, opts, deps) {
|
|
155
|
+
const { api, orgId } = await orgDeps(opts, deps);
|
|
156
|
+
const r = await api.request('GET', recordPath(orgId, domainName));
|
|
157
|
+
if (opts.json)
|
|
158
|
+
return printJson(r);
|
|
159
|
+
if (!r.items.length)
|
|
160
|
+
return info(`no records in ${domainName} — add one: insta domain records add ${domainName} A @ <ip>`);
|
|
161
|
+
for (const line of recordLines(r.items))
|
|
162
|
+
info(line);
|
|
163
|
+
}
|
|
164
|
+
export async function domainRecordsAdd(domainName, type, name, content, opts, deps) {
|
|
165
|
+
const body = { type: type.toUpperCase(), host: name, answer: content, ttl: whole('--ttl', opts.ttl), priority: whole('--priority', opts.priority) };
|
|
166
|
+
const { api, orgId } = await orgDeps(opts, deps);
|
|
167
|
+
const r = await api.request('POST', recordPath(orgId, domainName), body);
|
|
168
|
+
if (opts.json)
|
|
169
|
+
return printJson(r);
|
|
170
|
+
for (const line of recordLines([r]))
|
|
171
|
+
info(line);
|
|
172
|
+
}
|
|
173
|
+
export async function domainRecordsSet(domainName, id, opts, deps) {
|
|
174
|
+
const body = { type: opts.type?.toUpperCase(), host: opts.name, answer: opts.content, ttl: whole('--ttl', opts.ttl), priority: whole('--priority', opts.priority) };
|
|
175
|
+
if (Object.values(body).every((v) => v === undefined))
|
|
176
|
+
die('nothing to change — pass --type, --name, --content, --ttl or --priority');
|
|
177
|
+
const { api, orgId } = await orgDeps(opts, deps);
|
|
178
|
+
const r = await api.request('PATCH', `${recordPath(orgId, domainName)}/${encodeURIComponent(id)}`, body);
|
|
179
|
+
if (opts.json)
|
|
180
|
+
return printJson(r);
|
|
181
|
+
for (const line of recordLines([r]))
|
|
182
|
+
info(line);
|
|
183
|
+
}
|
|
184
|
+
export async function domainRecordsRemove(domainName, id, opts, deps) {
|
|
185
|
+
const { api, orgId } = await orgDeps(opts, deps);
|
|
186
|
+
const r = await api.request('DELETE', `${recordPath(orgId, domainName)}/${encodeURIComponent(id)}`);
|
|
187
|
+
if (opts.json)
|
|
188
|
+
return printJson(r);
|
|
189
|
+
info(`removed record ${id} from ${domainName}`);
|
|
190
|
+
}
|
|
141
191
|
//# sourceMappingURL=domain.js.map
|
package/dist/index.js
CHANGED
|
@@ -375,6 +375,21 @@ dom.command('list').description("Domains bought through InstaCloud in this org
|
|
|
375
375
|
.action(guard((o) => domainCmd.domainList(o)));
|
|
376
376
|
dom.command('status <name>').description("A bought domain's order and attach state").option('--json')
|
|
377
377
|
.action(guard((name, o) => domainCmd.domainStatus(name, o)));
|
|
378
|
+
const rec = dom.command('records').description('DNS records of a bought domain — the zone InstaCloud holds at the registrar');
|
|
379
|
+
rec.command('list <domain>').description('Every record in the zone, managed ones marked')
|
|
380
|
+
.option('--org <id>', "target org (default: linked project's org)").option('--json')
|
|
381
|
+
.action(guard((domain, o) => domainCmd.domainRecordsList(domain, o)));
|
|
382
|
+
rec.command('add <domain> <type> <name> <content>').description('Add a record — type A|AAAA|CNAME|ANAME|MX|TXT|SRV|NS; name "@" for the domain itself, a label like "www", or the full hostname under it')
|
|
383
|
+
.option('--ttl <seconds>', 'time to live in seconds (default 300)').option('--priority <n>', 'MX and SRV only')
|
|
384
|
+
.option('--org <id>', "target org (default: linked project's org)").option('--json')
|
|
385
|
+
.action(guard((domain, type, name, content, o) => domainCmd.domainRecordsAdd(domain, type, name, content, o)));
|
|
386
|
+
rec.command('set <domain> <id>').description('Change a record by its id (from `records list`); fields you omit keep their value')
|
|
387
|
+
.option('--type <t>', 'A|AAAA|CNAME|ANAME|MX|TXT|SRV|NS').option('--name <host>', '"@" for the domain itself, a label like "www", or the full hostname under it').option('--content <value>', 'the answer').option('--ttl <seconds>', 'time to live in seconds').option('--priority <n>', 'MX and SRV only')
|
|
388
|
+
.option('--org <id>', "target org (default: linked project's org)").option('--json')
|
|
389
|
+
.action(guard((domain, id, o) => domainCmd.domainRecordsSet(domain, id, o)));
|
|
390
|
+
rec.command('remove <domain> <id>').description('Remove a record by its id (a record InstaCloud published for a live hostname is refused)')
|
|
391
|
+
.option('--org <id>', "target org (default: linked project's org)").option('--json')
|
|
392
|
+
.action(guard((domain, id, o) => domainCmd.domainRecordsRemove(domain, id, o)));
|
|
378
393
|
const bill = program.command('billing').description('Current billing cycle overview (tier / used / included / overage / credits / forecast + per-dimension & per-project breakdown)')
|
|
379
394
|
.option('--org <id>', 'target org (default: linked project\'s org)').option('--json')
|
|
380
395
|
.action(guard((o) => billing(o)));
|