t44 0.2.0-rc.1
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.
Potentially problematic release.
This version of t44 might be problematic. Click here for more details.
- package/LICENSE.md +203 -0
- package/README.md +154 -0
- package/bin/activate +36 -0
- package/bin/activate.ts +30 -0
- package/bin/postinstall.sh +19 -0
- package/bin/shell +27 -0
- package/bin/t44 +27 -0
- package/caps/HomeRegistry.v0.ts +298 -0
- package/caps/OpenApiSchema.v0.ts +192 -0
- package/caps/ProjectDeployment.v0.ts +363 -0
- package/caps/ProjectDevelopment.v0.ts +246 -0
- package/caps/ProjectPublishing.v0.ts +307 -0
- package/caps/ProjectRack.v0.ts +128 -0
- package/caps/WorkspaceCli.v0.ts +391 -0
- package/caps/WorkspaceConfig.v0.ts +626 -0
- package/caps/WorkspaceConfig.yaml +53 -0
- package/caps/WorkspaceConnection.v0.ts +240 -0
- package/caps/WorkspaceEntityConfig.v0.ts +64 -0
- package/caps/WorkspaceEntityFact.v0.ts +193 -0
- package/caps/WorkspaceInfo.v0.ts +554 -0
- package/caps/WorkspaceInit.v0.ts +30 -0
- package/caps/WorkspaceKey.v0.ts +186 -0
- package/caps/WorkspaceProjects.v0.ts +455 -0
- package/caps/WorkspacePrompt.v0.ts +396 -0
- package/caps/WorkspaceShell.sh +39 -0
- package/caps/WorkspaceShell.v0.ts +104 -0
- package/caps/WorkspaceShell.yaml +65 -0
- package/caps/WorkspaceShellCli.v0.ts +109 -0
- package/caps/WorkspaceTest.v0.ts +167 -0
- package/caps/providers/LICENSE.md +8 -0
- package/caps/providers/README.md +2 -0
- package/caps/providers/bunny.net/ProjectDeployment.v0.ts +328 -0
- package/caps/providers/bunny.net/api-pull.v0.test.ts +319 -0
- package/caps/providers/bunny.net/api-pull.v0.ts +161 -0
- package/caps/providers/bunny.net/api-storage.v0.test.ts +168 -0
- package/caps/providers/bunny.net/api-storage.v0.ts +245 -0
- package/caps/providers/bunny.net/api.v0.ts +95 -0
- package/caps/providers/dynadot.com/ProjectDeployment.v0.ts +207 -0
- package/caps/providers/dynadot.com/api-domains.v0.test.ts +147 -0
- package/caps/providers/dynadot.com/api-domains.v0.ts +137 -0
- package/caps/providers/dynadot.com/api.v0.ts +88 -0
- package/caps/providers/git-scm.com/ProjectPublishing.v0.ts +231 -0
- package/caps/providers/github.com/ProjectPublishing.v0.ts +75 -0
- package/caps/providers/github.com/api.v0.ts +90 -0
- package/caps/providers/npmjs.com/ProjectPublishing.v0.ts +741 -0
- package/caps/providers/vercel.com/ProjectDeployment.v0.ts +339 -0
- package/caps/providers/vercel.com/api.v0.test.ts +67 -0
- package/caps/providers/vercel.com/api.v0.ts +132 -0
- package/caps/providers/vercel.com/bun.lock +194 -0
- package/caps/providers/vercel.com/package.json +10 -0
- package/caps/providers/vercel.com/project.v0.test.ts +108 -0
- package/caps/providers/vercel.com/project.v0.ts +150 -0
- package/caps/providers/vercel.com/tsconfig.json +28 -0
- package/docs/Overview.drawio +189 -0
- package/docs/Overview.svg +4 -0
- package/lib/crypto.ts +53 -0
- package/lib/openapi.ts +132 -0
- package/lib/ucan.ts +137 -0
- package/package.json +41 -0
- package/structs/HomeRegistryConfig.v0.ts +27 -0
- package/structs/ProjectDeploymentConfig.v0.ts +27 -0
- package/structs/ProjectDeploymentFact.v0.ts +110 -0
- package/structs/ProjectPublishingFact.v0.ts +69 -0
- package/structs/ProjectRackConfig.v0.ts +27 -0
- package/structs/WorkspaceCliConfig.v0.ts +27 -0
- package/structs/WorkspaceConfig.v0.ts +27 -0
- package/structs/WorkspaceKeyConfig.v0.ts +27 -0
- package/structs/WorkspaceMappings.v0.ts +27 -0
- package/structs/WorkspaceProjectsConfig.v0.ts +27 -0
- package/structs/WorkspaceRepositories.v0.ts +27 -0
- package/structs/WorkspaceShellConfig.v0.ts +45 -0
- package/structs/providers/LICENSE.md +8 -0
- package/structs/providers/README.md +2 -0
- package/structs/providers/bunny.net/ProjectDeploymentFact.v0.ts +41 -0
- package/structs/providers/bunny.net/WorkspaceConnectionConfig.v0.ts +42 -0
- package/structs/providers/dynadot.com/DomainFact.v0.ts +146 -0
- package/structs/providers/dynadot.com/WorkspaceConnectionConfig.v0.ts +41 -0
- package/structs/providers/git-scm.com/ProjectPublishingFact.v0.ts +46 -0
- package/structs/providers/github.com/ProjectPublishingFact.v0.ts +52 -0
- package/structs/providers/github.com/WorkspaceConnectionConfig.v0.ts +42 -0
- package/structs/providers/npmjs.com/ProjectPublishingFact.v0.ts +48 -0
- package/structs/providers/vercel.com/ProjectDeploymentFact.v0.ts +38 -0
- package/structs/providers/vercel.com/WorkspaceConnectionConfig.v0.ts +48 -0
- package/tsconfig.json +28 -0
- package/workspace-rt.ts +134 -0
- package/workspace.yaml +5 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
|
|
2
|
+
import uploadToBunny from 'upload-to-bunny'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export async function capsule({
|
|
6
|
+
encapsulate,
|
|
7
|
+
CapsulePropertyTypes,
|
|
8
|
+
makeImportStack
|
|
9
|
+
}: {
|
|
10
|
+
encapsulate: any
|
|
11
|
+
CapsulePropertyTypes: any
|
|
12
|
+
makeImportStack: any
|
|
13
|
+
}) {
|
|
14
|
+
|
|
15
|
+
// https://docs.bunny.net/api-reference/core/storage-zone/
|
|
16
|
+
return encapsulate({
|
|
17
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
18
|
+
'#@stream44.studio/encapsulate/structs/Capsule.v0': {},
|
|
19
|
+
'#t44/structs/providers/bunny.net/ProjectDeploymentFact.v0': {
|
|
20
|
+
as: '$ProjectDeploymentFact'
|
|
21
|
+
},
|
|
22
|
+
'#': {
|
|
23
|
+
|
|
24
|
+
api: {
|
|
25
|
+
type: CapsulePropertyTypes.Mapping,
|
|
26
|
+
value: './api.v0'
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
listZones: {
|
|
30
|
+
type: CapsulePropertyTypes.Function,
|
|
31
|
+
value: async function (this: any, options?: { page?: number; perPage?: number; search?: string }) {
|
|
32
|
+
const params: Record<string, string | number> = {};
|
|
33
|
+
if (options?.page !== undefined) {
|
|
34
|
+
params.page = options.page;
|
|
35
|
+
}
|
|
36
|
+
if (options?.perPage !== undefined) {
|
|
37
|
+
params.perPage = options.perPage;
|
|
38
|
+
}
|
|
39
|
+
if (options?.search) {
|
|
40
|
+
params.search = options.search;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const result = await this.api.call({
|
|
44
|
+
method: 'GET',
|
|
45
|
+
url: 'https://api.bunny.net/storagezone',
|
|
46
|
+
params,
|
|
47
|
+
operation: 'listZones'
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
await this.$ProjectDeploymentFact.set('storage-zones', 'list', 'StorageZoneModelList', result);
|
|
51
|
+
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
createZone: {
|
|
57
|
+
type: CapsulePropertyTypes.Function,
|
|
58
|
+
value: async function (this: any, options: {
|
|
59
|
+
name: string;
|
|
60
|
+
region: string;
|
|
61
|
+
replicationRegions?: string[];
|
|
62
|
+
zoneTier?: 'Standard' | 'Premium';
|
|
63
|
+
storageZoneType?: 'NotSupported' | 'Standard' | 'Premium';
|
|
64
|
+
}) {
|
|
65
|
+
return await this.api.call({
|
|
66
|
+
method: 'POST',
|
|
67
|
+
url: 'https://api.bunny.net/storagezone',
|
|
68
|
+
data: {
|
|
69
|
+
Name: options.name,
|
|
70
|
+
Region: options.region,
|
|
71
|
+
ReplicationRegions: options.replicationRegions || [],
|
|
72
|
+
ZoneTier: options.zoneTier || 'Standard',
|
|
73
|
+
StorageZoneType: options.storageZoneType || 'NotSupported'
|
|
74
|
+
},
|
|
75
|
+
operation: 'createZone'
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
ensureZone: {
|
|
81
|
+
type: CapsulePropertyTypes.Function,
|
|
82
|
+
value: async function (this: any, options: {
|
|
83
|
+
name: string;
|
|
84
|
+
region: string;
|
|
85
|
+
replicationRegions?: string[];
|
|
86
|
+
zoneTier?: 'Standard' | 'Premium';
|
|
87
|
+
storageZoneType?: 'NotSupported' | 'Standard' | 'Premium';
|
|
88
|
+
}) {
|
|
89
|
+
const zones = await this.listZones({ search: options.name });
|
|
90
|
+
const existingZone = zones.find((zone: any) => zone.Name === options.name);
|
|
91
|
+
|
|
92
|
+
if (existingZone) {
|
|
93
|
+
return await this.getZone(existingZone.Id);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return await this.createZone(options);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
getZone: {
|
|
101
|
+
type: CapsulePropertyTypes.Function,
|
|
102
|
+
value: async function (this: any, id: number) {
|
|
103
|
+
const storageZone = await this.api.call({
|
|
104
|
+
method: 'GET',
|
|
105
|
+
url: `https://api.bunny.net/storagezone/${id}`,
|
|
106
|
+
operation: 'getZone'
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (storageZone && storageZone.Name) {
|
|
110
|
+
await this.$ProjectDeploymentFact.set('storage-zones', storageZone.Name, 'StorageZoneModel', storageZone);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return storageZone;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
deleteZone: {
|
|
118
|
+
type: CapsulePropertyTypes.Function,
|
|
119
|
+
value: async function (this: any, id: number, deletePullZones: boolean = true) {
|
|
120
|
+
const params: Record<string, string | boolean> = {};
|
|
121
|
+
if (!deletePullZones) {
|
|
122
|
+
params.deletePullZones = false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return await this.api.call({
|
|
126
|
+
method: 'DELETE',
|
|
127
|
+
url: `https://api.bunny.net/storagezone/${id}`,
|
|
128
|
+
params,
|
|
129
|
+
operation: 'deleteZone'
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
listFiles: {
|
|
135
|
+
type: CapsulePropertyTypes.Function,
|
|
136
|
+
value: async function (this: any, options: {
|
|
137
|
+
storageZoneName: string;
|
|
138
|
+
storageHostname: string;
|
|
139
|
+
path?: string;
|
|
140
|
+
password: string;
|
|
141
|
+
}) {
|
|
142
|
+
const path = options.path ? `/${options.path}` : '';
|
|
143
|
+
const headers = {
|
|
144
|
+
'AccessKey': options.password
|
|
145
|
+
};
|
|
146
|
+
return await this.api.call({
|
|
147
|
+
method: 'GET',
|
|
148
|
+
url: `https://${options.storageHostname}/${options.storageZoneName}${path}/`,
|
|
149
|
+
operation: 'listFiles',
|
|
150
|
+
headers: headers
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
uploadFile: {
|
|
156
|
+
type: CapsulePropertyTypes.Function,
|
|
157
|
+
value: async function (this: any, options: {
|
|
158
|
+
storageZoneName: string;
|
|
159
|
+
storageHostname: string;
|
|
160
|
+
path?: string;
|
|
161
|
+
fileName: string;
|
|
162
|
+
data: string | Buffer;
|
|
163
|
+
password: string;
|
|
164
|
+
}) {
|
|
165
|
+
const path = options.path ? `/${options.path}` : '';
|
|
166
|
+
const headers = {
|
|
167
|
+
'AccessKey': options.password,
|
|
168
|
+
'Content-Type': 'application/octet-stream'
|
|
169
|
+
};
|
|
170
|
+
return await this.api.call({
|
|
171
|
+
method: 'PUT',
|
|
172
|
+
url: `https://${options.storageHostname}/${options.storageZoneName}${path}/${options.fileName}`,
|
|
173
|
+
data: options.data,
|
|
174
|
+
operation: 'uploadFile',
|
|
175
|
+
headers: headers
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
deleteFile: {
|
|
181
|
+
type: CapsulePropertyTypes.Function,
|
|
182
|
+
value: async function (this: any, options: {
|
|
183
|
+
storageZoneName: string;
|
|
184
|
+
storageHostname: string;
|
|
185
|
+
path?: string;
|
|
186
|
+
fileName: string;
|
|
187
|
+
password: string;
|
|
188
|
+
}) {
|
|
189
|
+
const path = options.path ? `/${options.path}` : '';
|
|
190
|
+
const headers = {
|
|
191
|
+
'AccessKey': options.password
|
|
192
|
+
};
|
|
193
|
+
return await this.api.call({
|
|
194
|
+
method: 'DELETE',
|
|
195
|
+
url: `https://${options.storageHostname}/${options.storageZoneName}${path}/${options.fileName}`,
|
|
196
|
+
operation: 'deleteFile',
|
|
197
|
+
headers: headers
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
uploadDirectory: {
|
|
203
|
+
type: CapsulePropertyTypes.Function,
|
|
204
|
+
value: async function (this: any, options: {
|
|
205
|
+
sourceDirectory: string;
|
|
206
|
+
destinationDirectory?: string;
|
|
207
|
+
storageZoneName: string;
|
|
208
|
+
password: string;
|
|
209
|
+
region?: string;
|
|
210
|
+
cleanDestination?: 'simple' | 'avoid-deletes';
|
|
211
|
+
maxConcurrentUploads?: number;
|
|
212
|
+
}) {
|
|
213
|
+
|
|
214
|
+
const uploadOptions: any = {
|
|
215
|
+
storageZoneName: options.storageZoneName,
|
|
216
|
+
accessKey: options.password,
|
|
217
|
+
maxConcurrentUploads: options.maxConcurrentUploads || 10
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
if (options.region) {
|
|
221
|
+
uploadOptions.region = options.region;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (options.cleanDestination) {
|
|
225
|
+
uploadOptions.cleanDestination = options.cleanDestination;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return await uploadToBunny(
|
|
229
|
+
options.sourceDirectory,
|
|
230
|
+
options.destinationDirectory || '',
|
|
231
|
+
uploadOptions
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}, {
|
|
239
|
+
importMeta: import.meta,
|
|
240
|
+
importStack: makeImportStack(),
|
|
241
|
+
capsuleName: capsule['#'],
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
capsule['#'] = 't44/caps/providers/bunny.net/api-storage.v0'
|
|
245
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
|
|
4
|
+
export async function capsule({
|
|
5
|
+
encapsulate,
|
|
6
|
+
CapsulePropertyTypes,
|
|
7
|
+
makeImportStack
|
|
8
|
+
}: {
|
|
9
|
+
encapsulate: any
|
|
10
|
+
CapsulePropertyTypes: any
|
|
11
|
+
makeImportStack: any
|
|
12
|
+
}) {
|
|
13
|
+
// https://docs.bunny.net/api-reference/core
|
|
14
|
+
return encapsulate({
|
|
15
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
16
|
+
'#@stream44.studio/encapsulate/structs/Capsule.v0': {},
|
|
17
|
+
'#t44/structs/providers/bunny.net/WorkspaceConnectionConfig.v0': {
|
|
18
|
+
as: '$ConnectionConfig'
|
|
19
|
+
},
|
|
20
|
+
'#': {
|
|
21
|
+
|
|
22
|
+
call: {
|
|
23
|
+
type: CapsulePropertyTypes.Function,
|
|
24
|
+
value: async function (this: any, options: {
|
|
25
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
26
|
+
url: string;
|
|
27
|
+
data?: any;
|
|
28
|
+
params?: Record<string, string | number | boolean>;
|
|
29
|
+
operation?: string;
|
|
30
|
+
headers?: Record<string, string>;
|
|
31
|
+
}) {
|
|
32
|
+
const apiKey = options.headers?.AccessKey || await this.$ConnectionConfig.getConfigValue('apiKey')
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const config: any = {
|
|
36
|
+
method: options.method,
|
|
37
|
+
url: options.url,
|
|
38
|
+
headers: options.headers || {
|
|
39
|
+
'AccessKey': apiKey
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
if (options.data) {
|
|
44
|
+
if (!config.headers['Content-Type']) {
|
|
45
|
+
config.headers['Content-Type'] = 'application/json';
|
|
46
|
+
}
|
|
47
|
+
config.data = options.data;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (options.params) {
|
|
51
|
+
config.params = options.params;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const response = await axios(config);
|
|
55
|
+
return response.data;
|
|
56
|
+
} catch (error: any) {
|
|
57
|
+
if (error.response) {
|
|
58
|
+
const errorData = error.response.data;
|
|
59
|
+
const status = error.response.status;
|
|
60
|
+
const statusText = error.response.statusText;
|
|
61
|
+
|
|
62
|
+
const operationName = options.operation || options.method;
|
|
63
|
+
console.error(`Bunny API Error [${operationName}]:`);
|
|
64
|
+
console.error(` Status: ${status} ${statusText}`);
|
|
65
|
+
console.error(` Response:`, JSON.stringify(errorData, null, 2));
|
|
66
|
+
|
|
67
|
+
let errorMessage = `Bunny API ${operationName} failed: ${status} ${statusText}`;
|
|
68
|
+
|
|
69
|
+
if (errorData && typeof errorData === 'object') {
|
|
70
|
+
if (errorData.Message) {
|
|
71
|
+
errorMessage += ` - ${errorData.Message}`;
|
|
72
|
+
} else if (errorData.ErrorKey) {
|
|
73
|
+
errorMessage += ` - ${errorData.ErrorKey}`;
|
|
74
|
+
} else {
|
|
75
|
+
errorMessage += ` - ${JSON.stringify(errorData)}`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
throw new Error(errorMessage);
|
|
80
|
+
}
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}, {
|
|
89
|
+
importMeta: import.meta,
|
|
90
|
+
importStack: makeImportStack(),
|
|
91
|
+
capsuleName: capsule['#'],
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
capsule['#'] = 't44/caps/providers/bunny.net/api.v0'
|
|
95
|
+
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
|
|
2
|
+
export async function capsule({
|
|
3
|
+
encapsulate,
|
|
4
|
+
CapsulePropertyTypes,
|
|
5
|
+
makeImportStack
|
|
6
|
+
}: {
|
|
7
|
+
encapsulate: any
|
|
8
|
+
CapsulePropertyTypes: any
|
|
9
|
+
makeImportStack: any
|
|
10
|
+
}) {
|
|
11
|
+
return encapsulate({
|
|
12
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
13
|
+
'#@stream44.studio/encapsulate/structs/Capsule.v0': {},
|
|
14
|
+
'#t44/structs/ProjectDeploymentFact.v0': {
|
|
15
|
+
as: '$StatusFact'
|
|
16
|
+
},
|
|
17
|
+
'#t44/structs/providers/dynadot.com/DomainFact.v0': {
|
|
18
|
+
as: '$DomainFact'
|
|
19
|
+
},
|
|
20
|
+
'#t44/structs/ProjectDeploymentConfig.v0': {
|
|
21
|
+
as: '$ProjectDeploymentConfig'
|
|
22
|
+
},
|
|
23
|
+
'#': {
|
|
24
|
+
domains: {
|
|
25
|
+
type: CapsulePropertyTypes.Mapping,
|
|
26
|
+
value: './api-domains.v0'
|
|
27
|
+
},
|
|
28
|
+
deploy: {
|
|
29
|
+
type: CapsulePropertyTypes.Function,
|
|
30
|
+
value: async function (this: any, { projectionDir, alias, config, workspaceProjectName }: { projectionDir: string, alias: string, config: any, workspaceProjectName?: string }) {
|
|
31
|
+
const domainConfig = config.provider.config.Domain
|
|
32
|
+
const domainName = domainConfig.name
|
|
33
|
+
const zones = domainConfig.zones || []
|
|
34
|
+
|
|
35
|
+
console.log(`Deploying DNS for '${domainName}' via Dynadot ...`)
|
|
36
|
+
|
|
37
|
+
// Process zones - resolve any jit() functions
|
|
38
|
+
let mainDomainRecord: { recordType: string; value: string } | undefined
|
|
39
|
+
const subdomainRecords: Array<{ subdomain: string; record_type: string; value: string }> = []
|
|
40
|
+
|
|
41
|
+
for (const zone of zones) {
|
|
42
|
+
let value = zone.value
|
|
43
|
+
// Resolve jit() function if value is a function
|
|
44
|
+
if (typeof value === 'function') {
|
|
45
|
+
value = await value()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const recordType = zone.type?.toLowerCase() || 'cname'
|
|
49
|
+
const subdomain = zone.subdomain || ''
|
|
50
|
+
|
|
51
|
+
if (subdomain === '' || subdomain === '@') {
|
|
52
|
+
// Root domain record
|
|
53
|
+
mainDomainRecord = {
|
|
54
|
+
recordType,
|
|
55
|
+
value
|
|
56
|
+
}
|
|
57
|
+
console.log(` ${domainName} -> ${value} (${recordType.toUpperCase()})`)
|
|
58
|
+
} else {
|
|
59
|
+
// Subdomain record
|
|
60
|
+
subdomainRecords.push({
|
|
61
|
+
subdomain,
|
|
62
|
+
record_type: recordType,
|
|
63
|
+
value
|
|
64
|
+
})
|
|
65
|
+
console.log(` ${subdomain}.${domainName} -> ${value} (${recordType.toUpperCase()})`)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(`Setting DNS records ...`)
|
|
70
|
+
const setResult = await this.domains.setDns({
|
|
71
|
+
name: domainName,
|
|
72
|
+
records: subdomainRecords,
|
|
73
|
+
mainDomain: mainDomainRecord
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
if (setResult?.SetDnsResponse?.Status !== 'success') {
|
|
77
|
+
throw new Error(`Failed to set DNS: ${setResult?.SetDnsResponse?.Error || 'Unknown error'}`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(`DNS deployment complete: https://${domainName}`)
|
|
81
|
+
|
|
82
|
+
// Write deployment status with updatedAt
|
|
83
|
+
const statusResult = {
|
|
84
|
+
projectName: domainName,
|
|
85
|
+
provider: 'dynadot.com',
|
|
86
|
+
status: 'READY',
|
|
87
|
+
publicUrl: `https://${domainName}`,
|
|
88
|
+
'#t44/structs/ProjectDeploymentConfig.v0': {
|
|
89
|
+
updatedAt: new Date().toISOString()
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
await this.$StatusFact.set('ProjectDeploymentStatus', domainName, 'ProjectDeploymentStatus', statusResult)
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
deprovision: {
|
|
96
|
+
type: CapsulePropertyTypes.Function,
|
|
97
|
+
value: async function (this: any, { config }: { config: any }) {
|
|
98
|
+
const domainName = config.provider.config.Domain.name
|
|
99
|
+
|
|
100
|
+
console.log(`Deprovisioning DNS for '${domainName}' from Dynadot ...`)
|
|
101
|
+
|
|
102
|
+
// Get current DNS records
|
|
103
|
+
const currentDns = await this.domains.getDns({ name: domainName })
|
|
104
|
+
const existingRecords = currentDns?.GetDnsResponse?.GetDns?.NameServerSettings?.SubDomains || []
|
|
105
|
+
|
|
106
|
+
// Remove CNAME for root domain
|
|
107
|
+
const filteredRecords = existingRecords.filter((r: any) => {
|
|
108
|
+
return !(r.subdomain === '' && r.record_type === 'cname')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
if (filteredRecords.length !== existingRecords.length) {
|
|
112
|
+
console.log(`Removing root CNAME record ...`)
|
|
113
|
+
await this.domains.setDns({
|
|
114
|
+
name: domainName,
|
|
115
|
+
records: filteredRecords
|
|
116
|
+
})
|
|
117
|
+
console.log(`Root CNAME record removed`)
|
|
118
|
+
} else {
|
|
119
|
+
console.log(`No root CNAME record found to remove`)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Delete fact files
|
|
123
|
+
console.log(`Deleting fact files ...`)
|
|
124
|
+
try {
|
|
125
|
+
await this.$DomainFact.delete('dns', domainName)
|
|
126
|
+
await this.$StatusFact.delete('ProjectDeploymentStatus', domainName)
|
|
127
|
+
console.log(`Fact files deleted`)
|
|
128
|
+
} catch (error: any) {
|
|
129
|
+
console.log(`Error deleting fact files: ${error.message}`)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log(`Deprovision complete`)
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
status: {
|
|
136
|
+
type: CapsulePropertyTypes.Function,
|
|
137
|
+
value: async function (this: any, { config, now, passive }: { config: any; now?: boolean; passive?: boolean }) {
|
|
138
|
+
const domainName = config.provider.config.Domain.name
|
|
139
|
+
|
|
140
|
+
if (!domainName) {
|
|
141
|
+
return {
|
|
142
|
+
projectName: domainName || 'unknown',
|
|
143
|
+
provider: 'dynadot.com',
|
|
144
|
+
error: 'No domain name configured',
|
|
145
|
+
rawDefinitionFilepaths: []
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const rawFilepaths = [
|
|
150
|
+
this.$DomainFact.getRelativeFilepath('dns', domainName)
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
// Try to get cached status if not forcing refresh
|
|
154
|
+
if (!now) {
|
|
155
|
+
const cached = await this.$StatusFact.get('ProjectDeploymentStatus', domainName, 'ProjectDeploymentStatus', rawFilepaths)
|
|
156
|
+
if (cached) {
|
|
157
|
+
return cached.data
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// In passive mode, don't call the provider if no cache exists
|
|
162
|
+
if (passive) {
|
|
163
|
+
return null
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const dnsInfo = await this.domains.getDns({ name: domainName })
|
|
167
|
+
const nsSettings = dnsInfo?.GetDnsResponse?.GetDns?.NameServerSettings || {}
|
|
168
|
+
const mainDomains = nsSettings.MainDomains || []
|
|
169
|
+
const subDomains = nsSettings.SubDomains || []
|
|
170
|
+
|
|
171
|
+
// Check for main domain CNAME record
|
|
172
|
+
const mainCname = mainDomains.find((r: any) => r.RecordType?.toLowerCase() === 'cname')
|
|
173
|
+
|
|
174
|
+
// Preserve updatedAt from existing cached status
|
|
175
|
+
const existingStatus = await this.$StatusFact.get('ProjectDeploymentStatus', domainName, 'ProjectDeploymentStatus')
|
|
176
|
+
const existingMeta = existingStatus?.data?.['#t44/structs/ProjectDeploymentConfig.v0']
|
|
177
|
+
|
|
178
|
+
const result: Record<string, any> = {
|
|
179
|
+
projectName: domainName,
|
|
180
|
+
provider: 'dynadot.com',
|
|
181
|
+
status: mainCname ? 'READY' : 'NOT_CONFIGURED',
|
|
182
|
+
publicUrl: mainCname ? `https://${domainName}` : undefined,
|
|
183
|
+
providerPortalUrl: `https://www.dynadot.com/account/domain/name/${domainName}`,
|
|
184
|
+
dnsRecords: { mainDomains, subDomains },
|
|
185
|
+
rawDefinitionFilepaths: rawFilepaths
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (existingMeta?.updatedAt) {
|
|
189
|
+
result['#t44/structs/ProjectDeploymentConfig.v0'] = {
|
|
190
|
+
updatedAt: existingMeta.updatedAt
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
await this.$StatusFact.set('ProjectDeploymentStatus', domainName, 'ProjectDeploymentStatus', result)
|
|
195
|
+
|
|
196
|
+
return result
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}, {
|
|
202
|
+
importMeta: import.meta,
|
|
203
|
+
importStack: makeImportStack(),
|
|
204
|
+
capsuleName: capsule['#'],
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
capsule['#'] = 't44/caps/providers/dynadot.com/ProjectDeployment.v0'
|