@superyachttimes/cli 1.1.1 → 1.3.0
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/aws/ec2.d.ts +26 -0
- package/dist/aws/ec2.d.ts.map +1 -0
- package/dist/aws/ec2.js +98 -0
- package/dist/aws/ec2.js.map +1 -0
- package/dist/aws/route53.d.ts +10 -0
- package/dist/aws/route53.d.ts.map +1 -0
- package/dist/aws/route53.js +78 -0
- package/dist/aws/route53.js.map +1 -0
- package/dist/cloudflare/api.d.ts +39 -0
- package/dist/cloudflare/api.d.ts.map +1 -0
- package/dist/cloudflare/api.js +108 -0
- package/dist/cloudflare/api.js.map +1 -0
- package/dist/cloudflare/urls.d.ts +4 -0
- package/dist/cloudflare/urls.d.ts.map +1 -0
- package/dist/cloudflare/urls.js +11 -0
- package/dist/cloudflare/urls.js.map +1 -0
- package/dist/commands/restart-ec2.d.ts +2 -0
- package/dist/commands/restart-ec2.d.ts.map +1 -0
- package/dist/commands/restart-ec2.js +56 -0
- package/dist/commands/restart-ec2.js.map +1 -0
- package/dist/commands/sync-ec2-ips-cloudflare.d.ts +2 -0
- package/dist/commands/sync-ec2-ips-cloudflare.d.ts.map +1 -0
- package/dist/commands/sync-ec2-ips-cloudflare.js +58 -0
- package/dist/commands/sync-ec2-ips-cloudflare.js.map +1 -0
- package/dist/commands/sync-ec2-route53.d.ts +2 -0
- package/dist/commands/sync-ec2-route53.d.ts.map +1 -0
- package/dist/commands/sync-ec2-route53.js +96 -0
- package/dist/commands/sync-ec2-route53.js.map +1 -0
- package/dist/config.d.ts +34 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +32 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface AwsOptions {
|
|
2
|
+
region: string;
|
|
3
|
+
profile: string;
|
|
4
|
+
}
|
|
5
|
+
export interface Ec2Instance {
|
|
6
|
+
instanceId: string;
|
|
7
|
+
name: string;
|
|
8
|
+
state: string;
|
|
9
|
+
publicIp: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface InstanceHealth {
|
|
12
|
+
systemStatus: string;
|
|
13
|
+
instanceStatus: string;
|
|
14
|
+
}
|
|
15
|
+
export interface RebootResult {
|
|
16
|
+
instanceId: string;
|
|
17
|
+
ok: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function listInstances(opts: AwsOptions): Promise<Ec2Instance[]>;
|
|
21
|
+
export declare function getInstanceStatuses(opts: AwsOptions): Promise<Map<string, InstanceHealth>>;
|
|
22
|
+
export declare function rebootInstances(opts: AwsOptions & {
|
|
23
|
+
instanceIds: string[];
|
|
24
|
+
}): Promise<RebootResult[]>;
|
|
25
|
+
export declare function collectEgressIps(opts: AwsOptions): Promise<string[]>;
|
|
26
|
+
//# sourceMappingURL=ec2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ec2.d.ts","sourceRoot":"","sources":["../../src/aws/ec2.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AASD,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAsB5E;AAED,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAqBtC;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,UAAU,GAAG;IAAE,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,GAC3C,OAAO,CAAC,YAAY,EAAE,CAAC,CAkBzB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAmC1E"}
|
package/dist/aws/ec2.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { EC2Client, DescribeInstancesCommand, DescribeInstanceStatusCommand, DescribeNatGatewaysCommand, RebootInstancesCommand, } from "@aws-sdk/client-ec2";
|
|
2
|
+
import { fromIni } from "@aws-sdk/credential-providers";
|
|
3
|
+
function createClient(opts) {
|
|
4
|
+
return new EC2Client({
|
|
5
|
+
region: opts.region,
|
|
6
|
+
credentials: fromIni({ profile: opts.profile }),
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export async function listInstances(opts) {
|
|
10
|
+
const client = createClient(opts);
|
|
11
|
+
const instances = [];
|
|
12
|
+
let nextToken;
|
|
13
|
+
do {
|
|
14
|
+
const res = await client.send(new DescribeInstancesCommand({ NextToken: nextToken }));
|
|
15
|
+
for (const reservation of res.Reservations ?? []) {
|
|
16
|
+
for (const instance of reservation.Instances ?? []) {
|
|
17
|
+
instances.push({
|
|
18
|
+
instanceId: instance.InstanceId ?? "",
|
|
19
|
+
name: instance.Tags?.find((t) => t.Key === "Name")?.Value ?? "(no name)",
|
|
20
|
+
state: instance.State?.Name ?? "unknown",
|
|
21
|
+
publicIp: instance.PublicIpAddress ?? null,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
nextToken = res.NextToken;
|
|
26
|
+
} while (nextToken);
|
|
27
|
+
return instances;
|
|
28
|
+
}
|
|
29
|
+
export async function getInstanceStatuses(opts) {
|
|
30
|
+
const client = createClient(opts);
|
|
31
|
+
const statuses = new Map();
|
|
32
|
+
let nextToken;
|
|
33
|
+
do {
|
|
34
|
+
const res = await client.send(new DescribeInstanceStatusCommand({
|
|
35
|
+
IncludeAllInstances: true,
|
|
36
|
+
NextToken: nextToken,
|
|
37
|
+
}));
|
|
38
|
+
for (const s of res.InstanceStatuses ?? []) {
|
|
39
|
+
if (!s.InstanceId)
|
|
40
|
+
continue;
|
|
41
|
+
statuses.set(s.InstanceId, {
|
|
42
|
+
systemStatus: s.SystemStatus?.Status ?? "unknown",
|
|
43
|
+
instanceStatus: s.InstanceStatus?.Status ?? "unknown",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
nextToken = res.NextToken;
|
|
47
|
+
} while (nextToken);
|
|
48
|
+
return statuses;
|
|
49
|
+
}
|
|
50
|
+
export async function rebootInstances(opts) {
|
|
51
|
+
const client = createClient(opts);
|
|
52
|
+
return Promise.all(opts.instanceIds.map(async (instanceId) => {
|
|
53
|
+
try {
|
|
54
|
+
await client.send(new RebootInstancesCommand({ InstanceIds: [instanceId] }));
|
|
55
|
+
return { instanceId, ok: true };
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
return {
|
|
59
|
+
instanceId,
|
|
60
|
+
ok: false,
|
|
61
|
+
error: err instanceof Error ? err.message : String(err),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
export async function collectEgressIps(opts) {
|
|
67
|
+
const client = createClient(opts);
|
|
68
|
+
const ips = new Set();
|
|
69
|
+
// Instance public IPs (instances that have one assigned).
|
|
70
|
+
let instanceToken;
|
|
71
|
+
do {
|
|
72
|
+
const res = await client.send(new DescribeInstancesCommand({ NextToken: instanceToken }));
|
|
73
|
+
for (const reservation of res.Reservations ?? []) {
|
|
74
|
+
for (const instance of reservation.Instances ?? []) {
|
|
75
|
+
if (instance.PublicIpAddress)
|
|
76
|
+
ips.add(instance.PublicIpAddress);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
instanceToken = res.NextToken;
|
|
80
|
+
} while (instanceToken);
|
|
81
|
+
// NAT gateway public IPs (covers private instances, Lambdas, etc. egressing
|
|
82
|
+
// through a NAT gateway). All available NAT gateways in the region.
|
|
83
|
+
let natToken;
|
|
84
|
+
do {
|
|
85
|
+
const res = await client.send(new DescribeNatGatewaysCommand({ NextToken: natToken }));
|
|
86
|
+
for (const nat of res.NatGateways ?? []) {
|
|
87
|
+
if (nat.State !== "available")
|
|
88
|
+
continue;
|
|
89
|
+
for (const addr of nat.NatGatewayAddresses ?? []) {
|
|
90
|
+
if (addr.PublicIp)
|
|
91
|
+
ips.add(addr.PublicIp);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
natToken = res.NextToken;
|
|
95
|
+
} while (natToken);
|
|
96
|
+
return Array.from(ips).sort();
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=ec2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ec2.js","sourceRoot":"","sources":["../../src/aws/ec2.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AwsOptions } from "./ec2.js";
|
|
2
|
+
export interface DesiredRecord {
|
|
3
|
+
name: string;
|
|
4
|
+
ip: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function slugify(name: string): string;
|
|
7
|
+
export declare function resolveHostedZoneId(opts: AwsOptions, zoneName: string): Promise<string>;
|
|
8
|
+
export declare function listARecords(opts: AwsOptions, zoneId: string): Promise<Map<string, string>>;
|
|
9
|
+
export declare function upsertARecords(opts: AwsOptions, zoneId: string, records: DesiredRecord[], ttl: number): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=route53.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route53.d.ts","sourceRoot":"","sources":["../../src/aws/route53.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAWD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM5C;AAED,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAYjB;AAGD,wBAAsB,YAAY,CAChC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA4B9B;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,EAAE,EACxB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CAmBf"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Route53Client, ListHostedZonesByNameCommand, ListResourceRecordSetsCommand, ChangeResourceRecordSetsCommand, } from "@aws-sdk/client-route-53";
|
|
2
|
+
import { fromIni } from "@aws-sdk/credential-providers";
|
|
3
|
+
function createClient(opts) {
|
|
4
|
+
return new Route53Client({
|
|
5
|
+
region: opts.region,
|
|
6
|
+
credentials: fromIni({ profile: opts.profile }),
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
// Lowercase; keep [a-z0-9_-]; replace any other run with "-";
|
|
10
|
+
// collapse repeated hyphens; trim leading/trailing hyphens.
|
|
11
|
+
export function slugify(name) {
|
|
12
|
+
return name
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.replace(/[^a-z0-9_-]+/g, "-")
|
|
15
|
+
.replace(/-+/g, "-")
|
|
16
|
+
.replace(/^-+|-+$/g, "");
|
|
17
|
+
}
|
|
18
|
+
export async function resolveHostedZoneId(opts, zoneName) {
|
|
19
|
+
const client = createClient(opts);
|
|
20
|
+
const target = zoneName.endsWith(".") ? zoneName : `${zoneName}.`;
|
|
21
|
+
const res = await client.send(new ListHostedZonesByNameCommand({ DNSName: target }));
|
|
22
|
+
const zone = (res.HostedZones ?? []).find((z) => z.Name === target);
|
|
23
|
+
if (!zone?.Id) {
|
|
24
|
+
throw new Error(`Route53 hosted zone "${zoneName}" not found.`);
|
|
25
|
+
}
|
|
26
|
+
// Id looks like "/hostedzone/Z123..."; strip the prefix.
|
|
27
|
+
return zone.Id.replace(/^\/hostedzone\//, "");
|
|
28
|
+
}
|
|
29
|
+
// Returns a map of fully-qualified A-record name (no trailing dot) -> IP value.
|
|
30
|
+
export async function listARecords(opts, zoneId) {
|
|
31
|
+
const client = createClient(opts);
|
|
32
|
+
const records = new Map();
|
|
33
|
+
let startName;
|
|
34
|
+
let startType;
|
|
35
|
+
let startIdentifier;
|
|
36
|
+
let truncated = true;
|
|
37
|
+
while (truncated) {
|
|
38
|
+
const res = await client.send(new ListResourceRecordSetsCommand({
|
|
39
|
+
HostedZoneId: zoneId,
|
|
40
|
+
StartRecordName: startName,
|
|
41
|
+
StartRecordType: startType,
|
|
42
|
+
StartRecordIdentifier: startIdentifier,
|
|
43
|
+
}));
|
|
44
|
+
for (const rr of res.ResourceRecordSets ?? []) {
|
|
45
|
+
if (rr.Type !== "A" || !rr.Name)
|
|
46
|
+
continue;
|
|
47
|
+
const ip = rr.ResourceRecords?.[0]?.Value;
|
|
48
|
+
if (!ip)
|
|
49
|
+
continue;
|
|
50
|
+
records.set(rr.Name.replace(/\.$/, ""), ip);
|
|
51
|
+
}
|
|
52
|
+
truncated = Boolean(res.IsTruncated);
|
|
53
|
+
startName = res.NextRecordName;
|
|
54
|
+
startType = res.NextRecordType;
|
|
55
|
+
startIdentifier = res.NextRecordIdentifier;
|
|
56
|
+
}
|
|
57
|
+
return records;
|
|
58
|
+
}
|
|
59
|
+
export async function upsertARecords(opts, zoneId, records, ttl) {
|
|
60
|
+
if (records.length === 0)
|
|
61
|
+
return;
|
|
62
|
+
const client = createClient(opts);
|
|
63
|
+
await client.send(new ChangeResourceRecordSetsCommand({
|
|
64
|
+
HostedZoneId: zoneId,
|
|
65
|
+
ChangeBatch: {
|
|
66
|
+
Changes: records.map((r) => ({
|
|
67
|
+
Action: "UPSERT",
|
|
68
|
+
ResourceRecordSet: {
|
|
69
|
+
Name: r.name,
|
|
70
|
+
Type: "A",
|
|
71
|
+
TTL: ttl,
|
|
72
|
+
ResourceRecords: [{ Value: r.ip }],
|
|
73
|
+
},
|
|
74
|
+
})),
|
|
75
|
+
},
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=route53.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route53.js","sourceRoot":"","sources":["../../src/aws/route53.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface CloudflareRule {
|
|
2
|
+
id: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
expression: string;
|
|
5
|
+
action: string;
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
action_parameters?: Record<string, unknown>;
|
|
8
|
+
logging?: Record<string, unknown>;
|
|
9
|
+
ref?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function resolveZoneId(opts: {
|
|
12
|
+
apiToken: string;
|
|
13
|
+
zoneName: string;
|
|
14
|
+
}): Promise<string>;
|
|
15
|
+
export declare function getCustomRuleByDescription(opts: {
|
|
16
|
+
apiToken: string;
|
|
17
|
+
zoneId: string;
|
|
18
|
+
description: string;
|
|
19
|
+
}): Promise<{
|
|
20
|
+
rulesetId: string;
|
|
21
|
+
rule: CloudflareRule;
|
|
22
|
+
}>;
|
|
23
|
+
export declare function updateRuleExpression(opts: {
|
|
24
|
+
apiToken: string;
|
|
25
|
+
zoneId: string;
|
|
26
|
+
rulesetId: string;
|
|
27
|
+
rule: CloudflareRule;
|
|
28
|
+
expression: string;
|
|
29
|
+
}): Promise<void>;
|
|
30
|
+
/** Builds a Cloudflare firewall expression matching the given source IPs. */
|
|
31
|
+
export declare function buildIpExpression(ips: string[]): string;
|
|
32
|
+
/**
|
|
33
|
+
* Extracts the IPs from a rule expression. Handles both forms Cloudflare uses:
|
|
34
|
+
* - set membership: `ip.src in {1.2.3.4 5.6.7.8}`
|
|
35
|
+
* - chained equality: `(ip.src eq 1.2.3.4) or (ip.src eq 5.6.7.8)`
|
|
36
|
+
* Returns null if neither form is present (caller falls back to showing the new set).
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseIpsFromExpression(expression: string): string[] | null;
|
|
39
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/cloudflare/api.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IAGjB,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAkDD,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQlB;AAED,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,cAAc,CAAA;CAAE,CAAC,CAsBvD;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,cAAc,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBhB;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,CAEvD;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAoB1E"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as urls from "./urls.js";
|
|
2
|
+
async function request(url, options) {
|
|
3
|
+
const headers = {
|
|
4
|
+
Authorization: `Bearer ${options.apiToken}`,
|
|
5
|
+
};
|
|
6
|
+
if (options.body !== undefined) {
|
|
7
|
+
headers["Content-Type"] = "application/json";
|
|
8
|
+
}
|
|
9
|
+
const res = await fetch(url, {
|
|
10
|
+
method: options.method ?? "GET",
|
|
11
|
+
headers,
|
|
12
|
+
body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
|
|
13
|
+
});
|
|
14
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
15
|
+
const payload = contentType.includes("application/json")
|
|
16
|
+
? await res.json()
|
|
17
|
+
: await res.text();
|
|
18
|
+
if (!res.ok) {
|
|
19
|
+
const detail = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
20
|
+
throw new Error(`Cloudflare API error: ${options.method ?? "GET"} ${url} → ${res.status} ${res.statusText}\n${detail}`);
|
|
21
|
+
}
|
|
22
|
+
const envelope = payload;
|
|
23
|
+
if (envelope && envelope.success === false) {
|
|
24
|
+
const msg = (envelope.errors ?? [])
|
|
25
|
+
.map((e) => `${e.code}: ${e.message}`)
|
|
26
|
+
.join("; ");
|
|
27
|
+
throw new Error(`Cloudflare API error: ${options.method ?? "GET"} ${url} → ${msg || "request unsuccessful"}`);
|
|
28
|
+
}
|
|
29
|
+
return envelope.result;
|
|
30
|
+
}
|
|
31
|
+
export async function resolveZoneId(opts) {
|
|
32
|
+
const zones = await request(urls.zonesByName(opts.zoneName), {
|
|
33
|
+
apiToken: opts.apiToken,
|
|
34
|
+
});
|
|
35
|
+
if (!zones || zones.length === 0) {
|
|
36
|
+
throw new Error(`No Cloudflare zone found for "${opts.zoneName}"`);
|
|
37
|
+
}
|
|
38
|
+
return zones[0].id;
|
|
39
|
+
}
|
|
40
|
+
export async function getCustomRuleByDescription(opts) {
|
|
41
|
+
const ruleset = await request(urls.customRulesEntrypoint(opts.zoneId), { apiToken: opts.apiToken });
|
|
42
|
+
const rules = ruleset.rules ?? [];
|
|
43
|
+
const matches = rules.filter((r) => r.description === opts.description);
|
|
44
|
+
if (matches.length === 0) {
|
|
45
|
+
const available = rules
|
|
46
|
+
.map((r) => `"${r.description ?? "(no description)"}"`)
|
|
47
|
+
.join(", ") || "(none)";
|
|
48
|
+
throw new Error(`No custom rule with description "${opts.description}" found. Available: ${available}`);
|
|
49
|
+
}
|
|
50
|
+
if (matches.length > 1) {
|
|
51
|
+
throw new Error(`Multiple custom rules match description "${opts.description}" (${matches.length}). Refusing to guess.`);
|
|
52
|
+
}
|
|
53
|
+
return { rulesetId: ruleset.id, rule: matches[0] };
|
|
54
|
+
}
|
|
55
|
+
export async function updateRuleExpression(opts) {
|
|
56
|
+
const body = {
|
|
57
|
+
action: opts.rule.action,
|
|
58
|
+
description: opts.rule.description,
|
|
59
|
+
enabled: opts.rule.enabled,
|
|
60
|
+
expression: opts.expression,
|
|
61
|
+
};
|
|
62
|
+
// Preserve fields that Cloudflare requires/keeps for the existing action.
|
|
63
|
+
// A "skip" rule, for example, is rejected without its action_parameters.
|
|
64
|
+
if (opts.rule.action_parameters !== undefined) {
|
|
65
|
+
body.action_parameters = opts.rule.action_parameters;
|
|
66
|
+
}
|
|
67
|
+
if (opts.rule.logging !== undefined) {
|
|
68
|
+
body.logging = opts.rule.logging;
|
|
69
|
+
}
|
|
70
|
+
if (opts.rule.ref !== undefined) {
|
|
71
|
+
body.ref = opts.rule.ref;
|
|
72
|
+
}
|
|
73
|
+
await request(urls.ruleUrl(opts.zoneId, opts.rulesetId, opts.rule.id), {
|
|
74
|
+
method: "PATCH",
|
|
75
|
+
apiToken: opts.apiToken,
|
|
76
|
+
body,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/** Builds a Cloudflare firewall expression matching the given source IPs. */
|
|
80
|
+
export function buildIpExpression(ips) {
|
|
81
|
+
return `(ip.src in {${ips.join(" ")}})`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Extracts the IPs from a rule expression. Handles both forms Cloudflare uses:
|
|
85
|
+
* - set membership: `ip.src in {1.2.3.4 5.6.7.8}`
|
|
86
|
+
* - chained equality: `(ip.src eq 1.2.3.4) or (ip.src eq 5.6.7.8)`
|
|
87
|
+
* Returns null if neither form is present (caller falls back to showing the new set).
|
|
88
|
+
*/
|
|
89
|
+
export function parseIpsFromExpression(expression) {
|
|
90
|
+
// Set membership form: ip.src in { ... }
|
|
91
|
+
const groupMatch = expression.match(/\{([^}]*)\}/);
|
|
92
|
+
if (groupMatch) {
|
|
93
|
+
return groupMatch[1]
|
|
94
|
+
.trim()
|
|
95
|
+
.split(/\s+/)
|
|
96
|
+
.filter((token) => token.length > 0);
|
|
97
|
+
}
|
|
98
|
+
// Chained equality form: ip.src eq <ip> [or ...]. The IP token allows
|
|
99
|
+
// digits/dots (IPv4), colons/hex (IPv6), and an optional /prefix (CIDR).
|
|
100
|
+
const eqMatches = [
|
|
101
|
+
...expression.matchAll(/ip\.src\s+eq\s+([0-9a-fA-F:.]+(?:\/\d+)?)/g),
|
|
102
|
+
];
|
|
103
|
+
if (eqMatches.length > 0) {
|
|
104
|
+
return eqMatches.map((m) => m[1]);
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/cloudflare/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urls.d.ts","sourceRoot":"","sources":["../../src/cloudflare/urls.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,MAAM,CAER"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const BASE = "https://api.cloudflare.com/client/v4";
|
|
2
|
+
export function zonesByName(name) {
|
|
3
|
+
return `${BASE}/zones?name=${encodeURIComponent(name)}`;
|
|
4
|
+
}
|
|
5
|
+
export function customRulesEntrypoint(zoneId) {
|
|
6
|
+
return `${BASE}/zones/${zoneId}/rulesets/phases/http_request_firewall_custom/entrypoint`;
|
|
7
|
+
}
|
|
8
|
+
export function ruleUrl(zoneId, rulesetId, ruleId) {
|
|
9
|
+
return `${BASE}/zones/${zoneId}/rulesets/${rulesetId}/rules/${ruleId}`;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=urls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urls.js","sourceRoot":"","sources":["../../src/cloudflare/urls.ts"],"names":[],"mappings":"AAAA,MAAM,IAAI,GAAG,sCAAsC,CAAC;AAEpD,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,GAAG,IAAI,eAAe,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAc;IAClD,OAAO,GAAG,IAAI,UAAU,MAAM,0DAA0D,CAAC;AAC3F,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,MAAc,EACd,SAAiB,EACjB,MAAc;IAEd,OAAO,GAAG,IAAI,UAAU,MAAM,aAAa,SAAS,UAAU,MAAM,EAAE,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart-ec2.d.ts","sourceRoot":"","sources":["../../src/commands/restart-ec2.ts"],"names":[],"mappings":"AAIA,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CA0DhD"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { checkbox, confirm } from "@inquirer/prompts";
|
|
2
|
+
import { loadConfig, getAwsConfig } from "../config.js";
|
|
3
|
+
import * as ec2 from "../aws/ec2.js";
|
|
4
|
+
export async function restartEc2() {
|
|
5
|
+
const config = loadConfig();
|
|
6
|
+
const aws = getAwsConfig(config);
|
|
7
|
+
console.log(`Fetching EC2 instances in ${aws.region}...`);
|
|
8
|
+
const instances = await ec2.listInstances(aws);
|
|
9
|
+
if (instances.length === 0) {
|
|
10
|
+
console.log(`No EC2 instances found in ${aws.region}.`);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const statuses = await ec2.getInstanceStatuses(aws);
|
|
14
|
+
const selected = await checkbox({
|
|
15
|
+
message: "Select instance(s) to reboot (space to toggle, enter to confirm)",
|
|
16
|
+
choices: instances.map((instance) => {
|
|
17
|
+
const health = statuses.get(instance.instanceId);
|
|
18
|
+
const status = health
|
|
19
|
+
? `${health.systemStatus}/${health.instanceStatus}`
|
|
20
|
+
: "n/a";
|
|
21
|
+
return {
|
|
22
|
+
name: `${instance.name} (${instance.instanceId}) — ${instance.state}, status: ${status}`,
|
|
23
|
+
value: instance.instanceId,
|
|
24
|
+
};
|
|
25
|
+
}),
|
|
26
|
+
});
|
|
27
|
+
if (selected.length === 0) {
|
|
28
|
+
console.log("Nothing selected.");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const confirmed = await confirm({
|
|
32
|
+
message: `Reboot ${selected.length} instance(s)? [${selected.join(", ")}]`,
|
|
33
|
+
default: false,
|
|
34
|
+
});
|
|
35
|
+
if (!confirmed) {
|
|
36
|
+
console.log("Aborted.");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
console.log("Rebooting...");
|
|
40
|
+
const results = await ec2.rebootInstances({ ...aws, instanceIds: selected });
|
|
41
|
+
let failed = 0;
|
|
42
|
+
for (const result of results) {
|
|
43
|
+
if (result.ok) {
|
|
44
|
+
console.log(` ✓ ${result.instanceId} reboot requested`);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
failed++;
|
|
48
|
+
console.error(` ✗ ${result.instanceId} failed: ${result.error}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (failed > 0) {
|
|
52
|
+
throw new Error(`${failed}/${results.length} instance(s) failed to reboot`);
|
|
53
|
+
}
|
|
54
|
+
console.log(`All ${results.length} instance(s) reboot requested successfully.`);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=restart-ec2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart-ec2.js","sourceRoot":"","sources":["../../src/commands/restart-ec2.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-ec2-ips-cloudflare.d.ts","sourceRoot":"","sources":["../../src/commands/sync-ec2-ips-cloudflare.ts"],"names":[],"mappings":"AAKA,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAwD5D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { confirm } from "@inquirer/prompts";
|
|
2
|
+
import { loadConfig, getAwsConfig, getCloudflareConfig } from "../config.js";
|
|
3
|
+
import * as ec2 from "../aws/ec2.js";
|
|
4
|
+
import * as cloudflare from "../cloudflare/api.js";
|
|
5
|
+
export async function syncEc2IpsToCloudflare() {
|
|
6
|
+
const config = loadConfig();
|
|
7
|
+
const aws = getAwsConfig(config);
|
|
8
|
+
const cf = getCloudflareConfig(config);
|
|
9
|
+
console.log(`Collecting EC2 egress IPs in ${aws.region}...`);
|
|
10
|
+
const ips = await ec2.collectEgressIps(aws);
|
|
11
|
+
if (ips.length === 0) {
|
|
12
|
+
throw new Error("No EC2 egress IPs found; refusing to write an empty rule.");
|
|
13
|
+
}
|
|
14
|
+
console.log(`Found ${ips.length} IP(s): ${ips.join(", ")}`);
|
|
15
|
+
console.log(`Resolving Cloudflare zone "${cf.zoneName}"...`);
|
|
16
|
+
const zoneId = await cloudflare.resolveZoneId(cf);
|
|
17
|
+
const { rulesetId, rule } = await cloudflare.getCustomRuleByDescription({
|
|
18
|
+
apiToken: cf.apiToken,
|
|
19
|
+
zoneId,
|
|
20
|
+
description: cf.ruleDescription,
|
|
21
|
+
});
|
|
22
|
+
const currentIps = cloudflare.parseIpsFromExpression(rule.expression);
|
|
23
|
+
if (currentIps === null) {
|
|
24
|
+
console.log(`Could not parse current IPs from expression: ${rule.expression}`);
|
|
25
|
+
console.log(`New IP set: ${ips.join(", ")}`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
const added = ips.filter((ip) => !currentIps.includes(ip));
|
|
29
|
+
const removed = currentIps.filter((ip) => !ips.includes(ip));
|
|
30
|
+
if (added.length === 0 && removed.length === 0) {
|
|
31
|
+
console.log("Already up to date — no changes.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
console.log(`Changes to "${cf.ruleDescription}":`);
|
|
35
|
+
for (const ip of added)
|
|
36
|
+
console.log(` + ${ip}`);
|
|
37
|
+
for (const ip of removed)
|
|
38
|
+
console.log(` - ${ip}`);
|
|
39
|
+
}
|
|
40
|
+
const confirmed = await confirm({
|
|
41
|
+
message: `Apply update to "${cf.ruleDescription}" (${ips.length} IP(s) total)?`,
|
|
42
|
+
default: false,
|
|
43
|
+
});
|
|
44
|
+
if (!confirmed) {
|
|
45
|
+
console.log("Aborted.");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const expression = cloudflare.buildIpExpression(ips);
|
|
49
|
+
await cloudflare.updateRuleExpression({
|
|
50
|
+
apiToken: cf.apiToken,
|
|
51
|
+
zoneId,
|
|
52
|
+
rulesetId,
|
|
53
|
+
rule,
|
|
54
|
+
expression,
|
|
55
|
+
});
|
|
56
|
+
console.log(`Updated rule "${cf.ruleDescription}" with ${ips.length} IP(s).`);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=sync-ec2-ips-cloudflare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-ec2-ips-cloudflare.js","sourceRoot":"","sources":["../../src/commands/sync-ec2-ips-cloudflare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-ec2-route53.d.ts","sourceRoot":"","sources":["../../src/commands/sync-ec2-route53.ts"],"names":[],"mappings":"AAKA,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAuGtD"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { checkbox } from "@inquirer/prompts";
|
|
2
|
+
import { loadConfig, getAwsConfig, getRoute53Config } from "../config.js";
|
|
3
|
+
import * as ec2 from "../aws/ec2.js";
|
|
4
|
+
import * as route53 from "../aws/route53.js";
|
|
5
|
+
export async function syncEc2ToRoute53() {
|
|
6
|
+
const config = loadConfig();
|
|
7
|
+
const aws = getAwsConfig(config);
|
|
8
|
+
const r53 = getRoute53Config(config);
|
|
9
|
+
console.log(`Listing running EC2 instances in ${aws.region}...`);
|
|
10
|
+
const instances = await ec2.listInstances(aws);
|
|
11
|
+
const running = instances.filter((i) => i.state === "running" && i.publicIp);
|
|
12
|
+
if (running.length === 0) {
|
|
13
|
+
console.log("No running instances with a public IP found. Nothing to do.");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
// Group by slug so we can detect collisions; skip unnamed instances.
|
|
17
|
+
const suffix = `${r53.subdomain}.${r53.zoneName}`;
|
|
18
|
+
const bySlug = new Map();
|
|
19
|
+
for (const inst of running) {
|
|
20
|
+
if (inst.name === "(no name)") {
|
|
21
|
+
console.log(`Skipping ${inst.instanceId}: no Name tag.`);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const slug = route53.slugify(inst.name);
|
|
25
|
+
if (!slug) {
|
|
26
|
+
console.log(`Skipping ${inst.instanceId}: name "${inst.name}" slugified to empty.`);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const list = bySlug.get(slug) ?? [];
|
|
30
|
+
list.push(inst);
|
|
31
|
+
bySlug.set(slug, list);
|
|
32
|
+
}
|
|
33
|
+
const desired = [];
|
|
34
|
+
for (const [slug, insts] of bySlug) {
|
|
35
|
+
if (insts.length > 1) {
|
|
36
|
+
console.log(`Skipping slug "${slug}": ${insts.length} instances share it (${insts
|
|
37
|
+
.map((i) => i.instanceId)
|
|
38
|
+
.join(", ")}).`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
desired.push({
|
|
42
|
+
name: `${slug}.${suffix}`,
|
|
43
|
+
ip: insts[0].publicIp,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (desired.length === 0) {
|
|
47
|
+
console.log("No records to sync after filtering. Nothing to do.");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
console.log(`Resolving hosted zone "${r53.zoneName}"...`);
|
|
51
|
+
const zoneId = await route53.resolveHostedZoneId(aws, r53.zoneName);
|
|
52
|
+
const existing = await route53.listARecords(aws, zoneId);
|
|
53
|
+
const toCreate = [];
|
|
54
|
+
const toUpdate = [];
|
|
55
|
+
let unchanged = 0;
|
|
56
|
+
for (const rec of desired) {
|
|
57
|
+
const current = existing.get(rec.name);
|
|
58
|
+
if (current === undefined)
|
|
59
|
+
toCreate.push(rec);
|
|
60
|
+
else if (current !== rec.ip)
|
|
61
|
+
toUpdate.push(rec);
|
|
62
|
+
else
|
|
63
|
+
unchanged++;
|
|
64
|
+
}
|
|
65
|
+
if (toCreate.length === 0 && toUpdate.length === 0) {
|
|
66
|
+
console.log(`All ${unchanged} record(s) already up to date — no changes.`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (unchanged > 0)
|
|
70
|
+
console.log(`${unchanged} record(s) already up to date.`);
|
|
71
|
+
const choices = [
|
|
72
|
+
...toCreate.map((r) => ({
|
|
73
|
+
name: `+ ${r.name} -> ${r.ip}`,
|
|
74
|
+
value: r,
|
|
75
|
+
checked: true,
|
|
76
|
+
})),
|
|
77
|
+
...toUpdate.map((r) => ({
|
|
78
|
+
name: `~ ${r.name} -> ${r.ip} (was ${existing.get(r.name)})`,
|
|
79
|
+
value: r,
|
|
80
|
+
checked: true,
|
|
81
|
+
})),
|
|
82
|
+
].sort((a, b) => a.value.name.localeCompare(b.value.name));
|
|
83
|
+
const selected = await checkbox({
|
|
84
|
+
message: `Select records to apply to ${r53.zoneName} (space toggles, enter confirms):`,
|
|
85
|
+
choices,
|
|
86
|
+
});
|
|
87
|
+
if (selected.length === 0) {
|
|
88
|
+
console.log("Nothing selected. Aborted.");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
await route53.upsertARecords(aws, zoneId, selected, r53.ttl);
|
|
92
|
+
const createdCount = selected.filter((r) => toCreate.includes(r)).length;
|
|
93
|
+
const updatedCount = selected.length - createdCount;
|
|
94
|
+
console.log(`Done. ${createdCount} created, ${updatedCount} updated.`);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=sync-ec2-route53.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-ec2-route53.js","sourceRoot":"","sources":["../../src/commands/sync-ec2-route53.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -5,10 +5,44 @@ export interface PortainerInstance {
|
|
|
5
5
|
portainerRegistryAuth?: string;
|
|
6
6
|
portainerContainerName: string;
|
|
7
7
|
}
|
|
8
|
+
export interface AwsConfig {
|
|
9
|
+
profile?: string;
|
|
10
|
+
region?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface CloudflareConfig {
|
|
13
|
+
apiToken: string;
|
|
14
|
+
zoneName?: string;
|
|
15
|
+
ruleDescription?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface Route53Config {
|
|
18
|
+
zoneName?: string;
|
|
19
|
+
subdomain?: string;
|
|
20
|
+
ttl?: number;
|
|
21
|
+
}
|
|
8
22
|
export interface Config {
|
|
9
23
|
sytApiUrl: string;
|
|
10
24
|
sytApiKey: string;
|
|
11
25
|
nodejsApi: PortainerInstance[];
|
|
26
|
+
aws?: AwsConfig;
|
|
27
|
+
cloudflare?: CloudflareConfig;
|
|
28
|
+
route53?: Route53Config;
|
|
12
29
|
}
|
|
13
30
|
export declare function loadConfig(): Config;
|
|
31
|
+
export interface ResolvedAwsConfig {
|
|
32
|
+
profile: string;
|
|
33
|
+
region: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ResolvedCloudflareConfig {
|
|
36
|
+
apiToken: string;
|
|
37
|
+
zoneName: string;
|
|
38
|
+
ruleDescription: string;
|
|
39
|
+
}
|
|
40
|
+
export declare function getAwsConfig(config: Config): ResolvedAwsConfig;
|
|
41
|
+
export declare function getCloudflareConfig(config: Config): ResolvedCloudflareConfig;
|
|
42
|
+
export interface ResolvedRoute53Config {
|
|
43
|
+
zoneName: string;
|
|
44
|
+
subdomain: string;
|
|
45
|
+
ttl: number;
|
|
46
|
+
}
|
|
47
|
+
export declare function getRoute53Config(config: Config): ResolvedRoute53Config;
|
|
14
48
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,MAAM;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,iBAAiB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,MAAM;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAID,wBAAgB,UAAU,IAAI,MAAM,CAmDnC;AAOD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAK9D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,CAa5E;AAMD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,CAOtE"}
|
package/dist/config.js
CHANGED
|
@@ -39,4 +39,36 @@ export function loadConfig() {
|
|
|
39
39
|
}
|
|
40
40
|
return config;
|
|
41
41
|
}
|
|
42
|
+
const DEFAULT_AWS_PROFILE = "syt";
|
|
43
|
+
const DEFAULT_AWS_REGION = "eu-west-1";
|
|
44
|
+
const DEFAULT_CLOUDFLARE_ZONE = "superyachttimes.com";
|
|
45
|
+
const DEFAULT_CLOUDFLARE_RULE = "Whitelist SYT EC2 Servers";
|
|
46
|
+
export function getAwsConfig(config) {
|
|
47
|
+
return {
|
|
48
|
+
profile: config.aws?.profile?.trim() || DEFAULT_AWS_PROFILE,
|
|
49
|
+
region: config.aws?.region?.trim() || DEFAULT_AWS_REGION,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function getCloudflareConfig(config) {
|
|
53
|
+
const apiToken = config.cloudflare?.apiToken;
|
|
54
|
+
if (apiToken == null || String(apiToken).trim().length === 0) {
|
|
55
|
+
throw new Error(`Config file at ${CONFIG_PATH} must contain a non-empty "cloudflare.apiToken" string`);
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
apiToken: String(apiToken).trim(),
|
|
59
|
+
zoneName: config.cloudflare?.zoneName?.trim() || DEFAULT_CLOUDFLARE_ZONE,
|
|
60
|
+
ruleDescription: config.cloudflare?.ruleDescription?.trim() || DEFAULT_CLOUDFLARE_RULE,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const DEFAULT_ROUTE53_ZONE = "superyachtapi.com";
|
|
64
|
+
const DEFAULT_ROUTE53_SUBDOMAIN = "infra";
|
|
65
|
+
const DEFAULT_ROUTE53_TTL = 300;
|
|
66
|
+
export function getRoute53Config(config) {
|
|
67
|
+
const ttl = config.route53?.ttl;
|
|
68
|
+
return {
|
|
69
|
+
zoneName: config.route53?.zoneName?.trim() || DEFAULT_ROUTE53_ZONE,
|
|
70
|
+
subdomain: config.route53?.subdomain?.trim() || DEFAULT_ROUTE53_SUBDOMAIN,
|
|
71
|
+
ttl: typeof ttl === "number" && ttl > 0 ? ttl : DEFAULT_ROUTE53_TTL,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
42
74
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,9 @@ import { select } from "@inquirer/prompts";
|
|
|
6
6
|
import { collapseLastModified } from "./commands/collapse-last-modified.js";
|
|
7
7
|
import { createApiKey } from "./commands/create-api-key.js";
|
|
8
8
|
import { recreateNodejsApi } from "./commands/recreate-nodejs-api.js";
|
|
9
|
+
import { restartEc2 } from "./commands/restart-ec2.js";
|
|
10
|
+
import { syncEc2IpsToCloudflare } from "./commands/sync-ec2-ips-cloudflare.js";
|
|
11
|
+
import { syncEc2ToRoute53 } from "./commands/sync-ec2-route53.js";
|
|
9
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
13
|
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
11
14
|
function printHelp() {
|
|
@@ -17,6 +20,9 @@ function printHelp() {
|
|
|
17
20
|
console.log(" collapse-last-modified Collapse last-modified yacht likes");
|
|
18
21
|
console.log(" create-api-key Create a new API key");
|
|
19
22
|
console.log(" recreate-nodejs-api Recreate Node.js API containers via Portainer");
|
|
23
|
+
console.log(" restart-ec2 Restart (reboot) one or more EC2 servers");
|
|
24
|
+
console.log(" sync-ec2-ips-cloudflare Sync EC2 egress IPs to a Cloudflare WAF rule");
|
|
25
|
+
console.log(" sync-ec2-route53 Sync running EC2 public IPs to Route53 infra records");
|
|
20
26
|
console.log();
|
|
21
27
|
console.log("Options:");
|
|
22
28
|
console.log(" -v, --version Show version");
|
|
@@ -43,6 +49,15 @@ async function runCommand(cmd) {
|
|
|
43
49
|
case "recreate-nodejs-api":
|
|
44
50
|
await recreateNodejsApi();
|
|
45
51
|
break;
|
|
52
|
+
case "restart-ec2":
|
|
53
|
+
await restartEc2();
|
|
54
|
+
break;
|
|
55
|
+
case "sync-ec2-ips-cloudflare":
|
|
56
|
+
await syncEc2IpsToCloudflare();
|
|
57
|
+
break;
|
|
58
|
+
case "sync-ec2-route53":
|
|
59
|
+
await syncEc2ToRoute53();
|
|
60
|
+
break;
|
|
46
61
|
default:
|
|
47
62
|
console.error(`Unknown command: ${cmd}`);
|
|
48
63
|
printHelp();
|
|
@@ -68,6 +83,21 @@ async function showMenu() {
|
|
|
68
83
|
value: "recreate-nodejs-api",
|
|
69
84
|
description: "Recreate Node.js API containers via Portainer",
|
|
70
85
|
},
|
|
86
|
+
{
|
|
87
|
+
name: "Restart EC2 servers",
|
|
88
|
+
value: "restart-ec2",
|
|
89
|
+
description: "Restart (reboot) one or more EC2 servers",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "Sync EC2 IPs to Cloudflare",
|
|
93
|
+
value: "sync-ec2-ips-cloudflare",
|
|
94
|
+
description: "Sync EC2 egress IPs to a Cloudflare WAF rule",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "Sync EC2 IPs to Route53",
|
|
98
|
+
value: "sync-ec2-route53",
|
|
99
|
+
description: "Sync running EC2 public IPs to Route53 infra records",
|
|
100
|
+
},
|
|
71
101
|
],
|
|
72
102
|
});
|
|
73
103
|
await runCommand(chosen);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superyachttimes/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "SuperYachtTimes CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"dev": "tsc --watch",
|
|
15
15
|
"clean": "rm -rf dist",
|
|
16
16
|
"prepublishOnly": "npm run clean && npm run build",
|
|
17
|
-
"push": "npm login && npm publish --scope @superyachttimes --access public"
|
|
17
|
+
"push": "npm login && npm publish --scope @superyachttimes --access public",
|
|
18
|
+
"ship": "bash scripts/ship.sh"
|
|
18
19
|
},
|
|
19
20
|
"engines": {
|
|
20
21
|
"node": ">=20"
|
|
@@ -24,6 +25,9 @@
|
|
|
24
25
|
"typescript": "^5"
|
|
25
26
|
},
|
|
26
27
|
"dependencies": {
|
|
28
|
+
"@aws-sdk/client-ec2": "^3.1053.0",
|
|
29
|
+
"@aws-sdk/client-route-53": "^3.1061.0",
|
|
30
|
+
"@aws-sdk/credential-providers": "^3.1053.0",
|
|
27
31
|
"@inquirer/prompts": "^8.2.0"
|
|
28
32
|
}
|
|
29
33
|
}
|