puls-dev 0.1.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/LICENSE +7 -0
- package/README.md +148 -0
- package/dist/core/checker.d.ts +5 -0
- package/dist/core/checker.js +148 -0
- package/dist/core/config.d.ts +35 -0
- package/dist/core/config.js +15 -0
- package/dist/core/decorators.d.ts +26 -0
- package/dist/core/decorators.js +86 -0
- package/dist/core/output.d.ts +8 -0
- package/dist/core/output.js +19 -0
- package/dist/core/resource.d.ts +20 -0
- package/dist/core/resource.js +77 -0
- package/dist/core/stack.d.ts +20 -0
- package/dist/core/stack.js +120 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/providers/aws/acm.d.ts +22 -0
- package/dist/providers/aws/acm.js +109 -0
- package/dist/providers/aws/api.d.ts +28 -0
- package/dist/providers/aws/api.js +36 -0
- package/dist/providers/aws/apigateway.d.ts +24 -0
- package/dist/providers/aws/apigateway.js +157 -0
- package/dist/providers/aws/cloudfront.d.ts +31 -0
- package/dist/providers/aws/cloudfront.js +205 -0
- package/dist/providers/aws/fargate.d.ts +43 -0
- package/dist/providers/aws/fargate.js +277 -0
- package/dist/providers/aws/index.d.ts +23 -0
- package/dist/providers/aws/index.js +29 -0
- package/dist/providers/aws/lambda.d.ts +30 -0
- package/dist/providers/aws/lambda.js +159 -0
- package/dist/providers/aws/list.d.ts +2 -0
- package/dist/providers/aws/list.js +44 -0
- package/dist/providers/aws/rds.d.ts +46 -0
- package/dist/providers/aws/rds.js +227 -0
- package/dist/providers/aws/route53.d.ts +38 -0
- package/dist/providers/aws/route53.js +218 -0
- package/dist/providers/aws/s3.d.ts +20 -0
- package/dist/providers/aws/s3.js +165 -0
- package/dist/providers/aws/secrets.d.ts +25 -0
- package/dist/providers/aws/secrets.js +151 -0
- package/dist/providers/aws/sqs.d.ts +33 -0
- package/dist/providers/aws/sqs.js +178 -0
- package/dist/providers/do/api.d.ts +11 -0
- package/dist/providers/do/api.js +52 -0
- package/dist/providers/do/certificate.d.ts +7 -0
- package/dist/providers/do/certificate.js +36 -0
- package/dist/providers/do/domain.d.ts +21 -0
- package/dist/providers/do/domain.js +81 -0
- package/dist/providers/do/droplet.d.ts +35 -0
- package/dist/providers/do/droplet.js +180 -0
- package/dist/providers/do/firewall.d.ts +23 -0
- package/dist/providers/do/firewall.js +94 -0
- package/dist/providers/do/index.d.ts +15 -0
- package/dist/providers/do/index.js +21 -0
- package/dist/providers/do/list.d.ts +2 -0
- package/dist/providers/do/list.js +59 -0
- package/dist/providers/do/load_balancer.d.ts +12 -0
- package/dist/providers/do/load_balancer.js +62 -0
- package/dist/providers/firebase/api.d.ts +4 -0
- package/dist/providers/firebase/api.js +62 -0
- package/dist/providers/firebase/auth.d.ts +35 -0
- package/dist/providers/firebase/auth.js +147 -0
- package/dist/providers/firebase/firestore.d.ts +28 -0
- package/dist/providers/firebase/firestore.js +120 -0
- package/dist/providers/firebase/functions.d.ts +50 -0
- package/dist/providers/firebase/functions.js +163 -0
- package/dist/providers/firebase/hosting.d.ts +14 -0
- package/dist/providers/firebase/hosting.js +144 -0
- package/dist/providers/firebase/index.d.ts +15 -0
- package/dist/providers/firebase/index.js +15 -0
- package/dist/providers/firebase/remoteconfig.d.ts +22 -0
- package/dist/providers/firebase/remoteconfig.js +135 -0
- package/dist/providers/firebase/storage.d.ts +34 -0
- package/dist/providers/firebase/storage.js +117 -0
- package/dist/providers/proxmox/api.d.ts +12 -0
- package/dist/providers/proxmox/api.js +50 -0
- package/dist/providers/proxmox/index.d.ts +15 -0
- package/dist/providers/proxmox/index.js +10 -0
- package/dist/providers/proxmox/list.d.ts +2 -0
- package/dist/providers/proxmox/list.js +15 -0
- package/dist/providers/proxmox/vm.d.ts +61 -0
- package/dist/providers/proxmox/vm.js +482 -0
- package/dist/types/aws.d.ts +55 -0
- package/dist/types/aws.js +48 -0
- package/dist/types/do.d.ts +19 -0
- package/dist/types/do.js +19 -0
- package/dist/types/gcp.d.ts +9 -0
- package/dist/types/gcp.js +9 -0
- package/dist/types/inventory.d.ts +87 -0
- package/dist/types/inventory.js +2 -0
- package/dist/types/proxmox.d.ts +11 -0
- package/dist/types/proxmox.js +28 -0
- package/package.json +56 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { BaseBuilder } from '../../core/resource.js';
|
|
3
|
+
import { cloudFetch, getProjectId } from './api.js';
|
|
4
|
+
const RULES_BASE = 'https://firebaserules.googleapis.com/v1';
|
|
5
|
+
const GCS_BASE = 'https://storage.googleapis.com/storage/v1';
|
|
6
|
+
export class FirebaseStorageBuilder extends BaseBuilder {
|
|
7
|
+
_rulesPath;
|
|
8
|
+
_cors = [];
|
|
9
|
+
_lifecycle;
|
|
10
|
+
_resolvedBucket;
|
|
11
|
+
constructor(bucket) {
|
|
12
|
+
// Bucket name resolved lazily in deploy() if not provided (needs projectId)
|
|
13
|
+
super(bucket ?? '__default__');
|
|
14
|
+
this._resolvedBucket = bucket;
|
|
15
|
+
this.discoveryPromise = Promise.resolve(null);
|
|
16
|
+
}
|
|
17
|
+
rules(filePath) { this._rulesPath = filePath; return this; }
|
|
18
|
+
cors(rules) { this._cors = rules; return this; }
|
|
19
|
+
lifecycle(rule) { this._lifecycle = rule; return this; }
|
|
20
|
+
bucket() {
|
|
21
|
+
return this._resolvedBucket ?? `${getProjectId()}.appspot.com`;
|
|
22
|
+
}
|
|
23
|
+
// ── Rules ─────────────────────────────────────────────────────────────────
|
|
24
|
+
releaseName() {
|
|
25
|
+
return `projects/${getProjectId()}/releases/firebase.storage/${this.bucket()}`;
|
|
26
|
+
}
|
|
27
|
+
async deployRules(dryRun) {
|
|
28
|
+
if (!this._rulesPath)
|
|
29
|
+
return;
|
|
30
|
+
const source = readFileSync(this._rulesPath, 'utf8');
|
|
31
|
+
if (dryRun) {
|
|
32
|
+
console.log(` 📝 [PLAN] Deploy Storage rules from "${this._rulesPath}" → ${this.bucket()}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const ruleset = await cloudFetch(RULES_BASE, `/projects/${getProjectId()}/rulesets`, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
body: JSON.stringify({ source: { files: [{ name: 'storage.rules', content: source }] } }),
|
|
38
|
+
});
|
|
39
|
+
await cloudFetch(RULES_BASE, `/${this.releaseName()}`, {
|
|
40
|
+
method: 'PUT',
|
|
41
|
+
body: JSON.stringify({ name: this.releaseName(), rulesetName: ruleset.name }),
|
|
42
|
+
});
|
|
43
|
+
console.log(` ✅ Storage rules deployed (ruleset: ${ruleset.name.split('/').pop()})`);
|
|
44
|
+
}
|
|
45
|
+
// ── CORS ──────────────────────────────────────────────────────────────────
|
|
46
|
+
async deployCors(dryRun) {
|
|
47
|
+
if (this._cors.length === 0)
|
|
48
|
+
return;
|
|
49
|
+
const corsBody = this._cors.map(r => ({
|
|
50
|
+
origin: r.origin,
|
|
51
|
+
method: r.method,
|
|
52
|
+
responseHeader: r.responseHeader ?? [],
|
|
53
|
+
maxAgeSeconds: r.maxAge ?? 3600,
|
|
54
|
+
}));
|
|
55
|
+
if (dryRun) {
|
|
56
|
+
console.log(` 📝 [PLAN] Set CORS on bucket "${this.bucket()}" (${this._cors.length} rule(s))`);
|
|
57
|
+
for (const r of this._cors) {
|
|
58
|
+
console.log(` └─ origins: [${r.origin.join(', ')}], methods: [${r.method.join(', ')}]`);
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
await cloudFetch(GCS_BASE, `/b/${this.bucket()}`, {
|
|
63
|
+
method: 'PATCH',
|
|
64
|
+
body: JSON.stringify({ cors: corsBody }),
|
|
65
|
+
});
|
|
66
|
+
console.log(` ✅ CORS configured on "${this.bucket()}"`);
|
|
67
|
+
}
|
|
68
|
+
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
|
69
|
+
async deployLifecycle(dryRun) {
|
|
70
|
+
if (!this._lifecycle)
|
|
71
|
+
return;
|
|
72
|
+
const rule = {
|
|
73
|
+
action: { type: 'Delete' },
|
|
74
|
+
condition: {},
|
|
75
|
+
};
|
|
76
|
+
if (this._lifecycle.deleteAfterDays !== undefined)
|
|
77
|
+
rule.condition.age = this._lifecycle.deleteAfterDays;
|
|
78
|
+
if (this._lifecycle.matchesPrefix?.length)
|
|
79
|
+
rule.condition.matchesPrefix = this._lifecycle.matchesPrefix;
|
|
80
|
+
if (dryRun) {
|
|
81
|
+
const parts = [];
|
|
82
|
+
if (this._lifecycle.deleteAfterDays)
|
|
83
|
+
parts.push(`delete after ${this._lifecycle.deleteAfterDays} days`);
|
|
84
|
+
if (this._lifecycle.matchesPrefix)
|
|
85
|
+
parts.push(`prefix: [${this._lifecycle.matchesPrefix.join(', ')}]`);
|
|
86
|
+
console.log(` 📝 [PLAN] Set lifecycle on "${this.bucket()}": ${parts.join(', ')}`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
await cloudFetch(GCS_BASE, `/b/${this.bucket()}`, {
|
|
90
|
+
method: 'PATCH',
|
|
91
|
+
body: JSON.stringify({ lifecycle: { rule: [rule] } }),
|
|
92
|
+
});
|
|
93
|
+
console.log(` ✅ Lifecycle configured on "${this.bucket()}"`);
|
|
94
|
+
}
|
|
95
|
+
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
|
96
|
+
async deploy() {
|
|
97
|
+
// Resolve bucket name now that projectId is available
|
|
98
|
+
this._resolvedBucket = this.bucket();
|
|
99
|
+
console.log(`\n🪣 Finalizing Firebase Storage "${this._resolvedBucket}"...`);
|
|
100
|
+
const dryRun = this.isDryRunActive();
|
|
101
|
+
await this.deployRules(dryRun);
|
|
102
|
+
await this.deployCors(dryRun);
|
|
103
|
+
await this.deployLifecycle(dryRun);
|
|
104
|
+
return { bucket: this._resolvedBucket, project: getProjectId() };
|
|
105
|
+
}
|
|
106
|
+
async destroy() {
|
|
107
|
+
const dryRun = this.isDryRunActive();
|
|
108
|
+
console.log(`\n🗑️ Destroying Firebase Storage config "${this.bucket()}"...`);
|
|
109
|
+
if (dryRun) {
|
|
110
|
+
console.log(` ℹ️ Storage buckets cannot be deleted via API — remove manually in the GCP console`);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.log(` ℹ️ Storage buckets cannot be deleted via API — remove manually in the GCP console`);
|
|
114
|
+
}
|
|
115
|
+
return { destroyed: this.bucket() };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class ProxmoxApiClient {
|
|
2
|
+
private baseUrl;
|
|
3
|
+
private authToken;
|
|
4
|
+
private dispatcher?;
|
|
5
|
+
constructor(url: string, user: string, tokenName: string, tokenSecret: string, verifySsl?: boolean);
|
|
6
|
+
private request;
|
|
7
|
+
get<T>(path: string): Promise<T>;
|
|
8
|
+
post<T>(path: string, body?: unknown): Promise<T>;
|
|
9
|
+
put<T>(path: string, body?: unknown): Promise<T>;
|
|
10
|
+
delete(path: string): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export declare function getPMClient(): ProxmoxApiClient;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Agent } from "undici";
|
|
2
|
+
import { Config } from "../../core/config.js";
|
|
3
|
+
export class ProxmoxApiClient {
|
|
4
|
+
baseUrl;
|
|
5
|
+
authToken;
|
|
6
|
+
dispatcher;
|
|
7
|
+
constructor(url, user, tokenName, tokenSecret, verifySsl = true) {
|
|
8
|
+
this.baseUrl = `${url.replace(/\/$/, "")}/api2/json`;
|
|
9
|
+
// PVE token format: USER@REALM!TOKENNAME=SECRET
|
|
10
|
+
this.authToken = `${user}!${tokenName}=${tokenSecret}`;
|
|
11
|
+
if (!verifySsl) {
|
|
12
|
+
this.dispatcher = new Agent({ connect: { rejectUnauthorized: false } });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async request(method, path, body) {
|
|
16
|
+
const headers = {
|
|
17
|
+
Authorization: `PVEAPIToken=${this.authToken}`,
|
|
18
|
+
};
|
|
19
|
+
if (body !== undefined)
|
|
20
|
+
headers['Content-Type'] = 'application/json';
|
|
21
|
+
const opts = { method, headers };
|
|
22
|
+
if (body !== undefined)
|
|
23
|
+
opts.body = JSON.stringify(body);
|
|
24
|
+
if (this.dispatcher)
|
|
25
|
+
opts.dispatcher = this.dispatcher;
|
|
26
|
+
const res = await fetch(`${this.baseUrl}${path}`, opts);
|
|
27
|
+
if (!res.ok)
|
|
28
|
+
throw new Error(`Proxmox ${method} ${path}: ${res.status} ${await res.text()}`);
|
|
29
|
+
const json = (await res.json());
|
|
30
|
+
return json.data ?? null;
|
|
31
|
+
}
|
|
32
|
+
async get(path) {
|
|
33
|
+
return this.request("GET", path);
|
|
34
|
+
}
|
|
35
|
+
async post(path, body) {
|
|
36
|
+
return this.request("POST", path, body);
|
|
37
|
+
}
|
|
38
|
+
async put(path, body) {
|
|
39
|
+
return this.request("PUT", path, body);
|
|
40
|
+
}
|
|
41
|
+
async delete(path) {
|
|
42
|
+
await this.request("DELETE", path);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function getPMClient() {
|
|
46
|
+
const cfg = Config.get().providers.proxmox;
|
|
47
|
+
if (!cfg?.url || !cfg?.user || !cfg?.tokenName || !cfg?.tokenSecret)
|
|
48
|
+
throw new Error("Proxmox not configured. Set proxmox: { url, user, tokenName, tokenSecret } in @Deploy");
|
|
49
|
+
return new ProxmoxApiClient(cfg.url, cfg.user, cfg.tokenName, cfg.tokenSecret, cfg.verifySsl ?? true);
|
|
50
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { VMBuilder } from "./vm.js";
|
|
2
|
+
export declare const Proxmox: {
|
|
3
|
+
init: (opts: {
|
|
4
|
+
url: string;
|
|
5
|
+
user: string;
|
|
6
|
+
tokenName: string;
|
|
7
|
+
tokenSecret: string;
|
|
8
|
+
nodes?: string[];
|
|
9
|
+
storage?: string;
|
|
10
|
+
dnsDomain?: string;
|
|
11
|
+
dnsServers?: string[];
|
|
12
|
+
verifySsl?: boolean;
|
|
13
|
+
}) => void;
|
|
14
|
+
VM: (name: string) => VMBuilder;
|
|
15
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getPMClient } from './api.js';
|
|
2
|
+
export async function listProxmoxVMs() {
|
|
3
|
+
const resources = await getPMClient().get('/cluster/resources?type=vm');
|
|
4
|
+
const vms = (resources ?? [])
|
|
5
|
+
.filter((r) => r.template !== 1)
|
|
6
|
+
.map((r) => ({
|
|
7
|
+
name: r.name,
|
|
8
|
+
vmid: r.vmid,
|
|
9
|
+
node: r.node,
|
|
10
|
+
status: r.status,
|
|
11
|
+
maxmem: r.maxmem ?? 0,
|
|
12
|
+
maxdisk: r.maxdisk ?? 0,
|
|
13
|
+
}));
|
|
14
|
+
return { vms };
|
|
15
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { BaseBuilder } from "../../core/resource.js";
|
|
2
|
+
import { Output } from "../../core/output.js";
|
|
3
|
+
import type { OSImage } from "../../types/proxmox.js";
|
|
4
|
+
export declare class VMBuilder extends BaseBuilder {
|
|
5
|
+
readonly out: {
|
|
6
|
+
ip: Output<string>;
|
|
7
|
+
vmid: Output<number>;
|
|
8
|
+
};
|
|
9
|
+
resolvedVmid: number | null;
|
|
10
|
+
resolvedNode: string | null;
|
|
11
|
+
resolvedIp: string | null;
|
|
12
|
+
private _image?;
|
|
13
|
+
private _cores;
|
|
14
|
+
private _memory;
|
|
15
|
+
private _provision?;
|
|
16
|
+
private _replace?;
|
|
17
|
+
private _node?;
|
|
18
|
+
private _storage?;
|
|
19
|
+
private _vlan?;
|
|
20
|
+
private _ip?;
|
|
21
|
+
private _sshKeys?;
|
|
22
|
+
constructor(name: string);
|
|
23
|
+
private discoverVm;
|
|
24
|
+
image(os: OSImage): this;
|
|
25
|
+
cores(n: number): this;
|
|
26
|
+
memory(mb: number): this;
|
|
27
|
+
provision(playbookPath: string): this;
|
|
28
|
+
replace(oldVmName: string): this;
|
|
29
|
+
node(n: string): this;
|
|
30
|
+
storage(pool: string): this;
|
|
31
|
+
vlan(tag: number): this;
|
|
32
|
+
ip(address: string): this;
|
|
33
|
+
sshKey(keys: string | readonly string[]): this;
|
|
34
|
+
deploy(): Promise<{
|
|
35
|
+
name: string;
|
|
36
|
+
vmid: number | null;
|
|
37
|
+
node: string | null;
|
|
38
|
+
ip?: undefined;
|
|
39
|
+
} | {
|
|
40
|
+
name: string;
|
|
41
|
+
vmid: string;
|
|
42
|
+
node?: undefined;
|
|
43
|
+
ip?: undefined;
|
|
44
|
+
} | {
|
|
45
|
+
name: string;
|
|
46
|
+
vmid: number;
|
|
47
|
+
ip: string | null;
|
|
48
|
+
node?: undefined;
|
|
49
|
+
}>;
|
|
50
|
+
destroy(): Promise<any>;
|
|
51
|
+
private waitForTask;
|
|
52
|
+
private destroyVmByName;
|
|
53
|
+
private resolvePublicKeys;
|
|
54
|
+
private checkCloudInit;
|
|
55
|
+
private checkPort;
|
|
56
|
+
private sshKeyPath;
|
|
57
|
+
private runProvisioner;
|
|
58
|
+
private runShellScript;
|
|
59
|
+
private runPuppet;
|
|
60
|
+
private runAnsible;
|
|
61
|
+
}
|