@highstate/backend-api 0.9.16 → 0.9.19
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/highstate.manifest.json +1 -1
- package/dist/index.js +7907 -29
- package/dist/index.js.map +1 -1
- package/package.json +6 -10
- package/src/handlers/instance.ts +20 -8
- package/src/handlers/worker.ts +50 -26
- package/src/index.ts +11 -2
- package/src/shared/authentication.ts +3 -4
- package/src/shared/error-handling.ts +2 -2
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@highstate/backend-api",
|
3
|
-
"version": "0.9.
|
3
|
+
"version": "0.9.19",
|
4
4
|
"type": "module",
|
5
5
|
"files": [
|
6
6
|
"dist",
|
@@ -17,8 +17,7 @@
|
|
17
17
|
"access": "public"
|
18
18
|
},
|
19
19
|
"scripts": {
|
20
|
-
"build": "highstate build"
|
21
|
-
"generate-sdk": "./scripts/generate-sdk.sh"
|
20
|
+
"build": "highstate build"
|
22
21
|
},
|
23
22
|
"peerDependencies": {
|
24
23
|
"@pulumi/pulumi": "^3.163.0",
|
@@ -32,18 +31,15 @@
|
|
32
31
|
"optional": true
|
33
32
|
}
|
34
33
|
},
|
35
|
-
"devDependencies": {
|
36
|
-
"ts-proto": "^2.7.5"
|
37
|
-
},
|
38
34
|
"dependencies": {
|
39
35
|
"@bufbuild/protobuf": "^2.6.1",
|
40
|
-
"@highstate/api": "^0.9.
|
41
|
-
"@highstate/backend": "^0.9.
|
42
|
-
"@highstate/cli": "^0.9.
|
36
|
+
"@highstate/api": "^0.9.19",
|
37
|
+
"@highstate/backend": "^0.9.19",
|
38
|
+
"@highstate/cli": "^0.9.19",
|
43
39
|
"abort-controller-x": "^0.4.3",
|
44
40
|
"nice-grpc": "^2.1.12",
|
45
41
|
"nice-grpc-common": "^2.0.2",
|
46
42
|
"zod": "^4.0.5"
|
47
43
|
},
|
48
|
-
"gitHead": "
|
44
|
+
"gitHead": "e77d292335556c6e5b6275acda1a3d1609d786a1"
|
49
45
|
}
|
package/src/handlers/instance.ts
CHANGED
@@ -1,32 +1,44 @@
|
|
1
|
-
import type { Services } from "@highstate/backend"
|
2
1
|
import type { InstanceServiceImplementation } from "@highstate/api/instance.v1"
|
3
|
-
import {
|
2
|
+
import type { Services } from "@highstate/backend"
|
3
|
+
import { instanceCustomStatusInputSchema } from "@highstate/backend/shared"
|
4
|
+
import { z } from "@highstate/contract"
|
4
5
|
import { authenticate, parseArgument } from "../shared"
|
5
6
|
|
6
7
|
export function createInstanceService(services: Services): InstanceServiceImplementation {
|
7
8
|
return {
|
8
9
|
async updateCustomStatus(request, context) {
|
9
|
-
const [projectId] = await authenticate(services, context)
|
10
|
+
const [projectId, apiKey] = await authenticate(services, context)
|
10
11
|
|
11
12
|
// TODO: validate instance access
|
12
13
|
|
13
|
-
const
|
14
|
+
const stateId = parseArgument(request, "stateId", z.cuid2())
|
15
|
+
const customStatus = parseArgument(request, "status", instanceCustomStatusInputSchema)
|
14
16
|
|
15
17
|
await services.instanceStateService.updateCustomStatus(
|
16
18
|
projectId,
|
17
|
-
|
19
|
+
stateId,
|
20
|
+
apiKey.serviceAccountId,
|
18
21
|
customStatus,
|
19
22
|
)
|
20
23
|
|
21
24
|
return {}
|
22
25
|
},
|
23
26
|
|
24
|
-
async removeCustomStatus(request,
|
27
|
+
async removeCustomStatus(request, context) {
|
28
|
+
const [projectId, apiKey] = await authenticate(services, context)
|
29
|
+
|
30
|
+
// TODO: validate instance access
|
31
|
+
|
32
|
+
const stateId = parseArgument(request, "stateId", z.cuid2())
|
33
|
+
|
25
34
|
await services.instanceStateService.removeCustomStatus(
|
26
|
-
|
27
|
-
|
35
|
+
projectId,
|
36
|
+
stateId,
|
37
|
+
apiKey.serviceAccountId,
|
28
38
|
request.statusName,
|
29
39
|
)
|
40
|
+
|
41
|
+
return {}
|
30
42
|
},
|
31
43
|
}
|
32
44
|
}
|
package/src/handlers/worker.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
import type { Services } from "@highstate/backend"
|
2
1
|
import type { WorkerServiceImplementation } from "@highstate/api/worker.v1"
|
3
|
-
import {
|
2
|
+
import type { Services } from "@highstate/backend"
|
3
|
+
import { commonObjectMetaSchema, z } from "@highstate/contract"
|
4
4
|
import { authenticate, parseArgument } from "../shared"
|
5
5
|
|
6
6
|
export function createWorkerService(services: Services): WorkerServiceImplementation {
|
@@ -8,48 +8,72 @@ export function createWorkerService(services: Services): WorkerServiceImplementa
|
|
8
8
|
async *connect(request, context) {
|
9
9
|
const [projectId] = await authenticate(services, context)
|
10
10
|
|
11
|
-
|
12
|
-
const workerId = parseArgument(request, "workerId", workerSchema.shape.id)
|
11
|
+
const workerVersionId = parseArgument(request, "workerVersionId", z.cuid2())
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
projectId,
|
17
|
-
workerId,
|
18
|
-
])
|
13
|
+
// set worker as running
|
14
|
+
services.workerManager.setWorkerRunning(projectId, workerVersionId)
|
19
15
|
|
20
|
-
//
|
21
|
-
await services.
|
22
|
-
await
|
16
|
+
// get existing registrations for this worker version
|
17
|
+
const database = await services.database.forProject(projectId)
|
18
|
+
const existingRegistrations = await database.workerUnitRegistration.findMany({
|
19
|
+
where: { workerVersionId },
|
20
|
+
select: { stateId: true, params: true },
|
21
|
+
})
|
23
22
|
|
24
23
|
// emit existing registrations
|
25
|
-
const
|
26
|
-
.getWorkerRegistrationIndexRepository(projectId, workerId)
|
27
|
-
.getAllItems()
|
28
|
-
|
29
|
-
for (const registration of registrations) {
|
24
|
+
for (const registration of existingRegistrations) {
|
30
25
|
yield {
|
31
26
|
event: {
|
32
27
|
$case: "unitRegistration",
|
33
28
|
value: {
|
34
|
-
|
29
|
+
stateId: registration.stateId,
|
35
30
|
params: registration.params,
|
36
31
|
},
|
37
32
|
},
|
38
33
|
}
|
39
34
|
}
|
40
35
|
|
41
|
-
//
|
36
|
+
// subscribe to new registration events for this worker version
|
37
|
+
const registrationStream = await services.pubsubManager.subscribe([
|
38
|
+
"worker-unit-registration",
|
39
|
+
projectId,
|
40
|
+
workerVersionId,
|
41
|
+
])
|
42
|
+
|
43
|
+
// emit new registration/deregistration events
|
42
44
|
for await (const event of registrationStream) {
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
if (event.type === "registered") {
|
46
|
+
yield {
|
47
|
+
event: {
|
48
|
+
$case: "unitRegistration",
|
49
|
+
value: {
|
50
|
+
instanceId: event.instanceId,
|
51
|
+
params: event.params,
|
52
|
+
},
|
49
53
|
},
|
50
|
-
}
|
54
|
+
}
|
55
|
+
} else if (event.type === "deregistered") {
|
56
|
+
yield {
|
57
|
+
event: {
|
58
|
+
$case: "unitDeregistration",
|
59
|
+
value: {
|
60
|
+
instanceId: event.instanceId,
|
61
|
+
},
|
62
|
+
},
|
63
|
+
}
|
51
64
|
}
|
52
65
|
}
|
53
66
|
},
|
67
|
+
|
68
|
+
async updateWorkerVersionMeta(request, context) {
|
69
|
+
const [projectId] = await authenticate(services, context)
|
70
|
+
|
71
|
+
const workerVersionId = parseArgument(request, "workerVersionId", z.string())
|
72
|
+
const meta = parseArgument(request, "meta", commonObjectMetaSchema)
|
73
|
+
|
74
|
+
await services.workerService.updateWorkerVersionMeta(projectId, workerVersionId, meta)
|
75
|
+
|
76
|
+
return {}
|
77
|
+
},
|
54
78
|
}
|
55
79
|
}
|
package/src/index.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
import { rm } from "node:fs/promises"
|
1
2
|
import { createServer } from "nice-grpc"
|
2
|
-
import {
|
3
|
+
import type { Services } from "@highstate/backend"
|
3
4
|
import { InstanceServiceDefinition } from "@highstate/api/instance.v1"
|
4
5
|
import { SecretServiceDefinition } from "@highstate/api/secret.v1"
|
5
6
|
import { WorkerServiceDefinition } from "@highstate/api/worker.v1"
|
@@ -16,9 +17,17 @@ export async function startBackedApi(services: Services) {
|
|
16
17
|
server.add(SecretServiceDefinition, createSecretService(services))
|
17
18
|
server.add(WorkerServiceDefinition, createWorkerService(services))
|
18
19
|
|
19
|
-
const uid = process.geteuid
|
20
|
+
const uid = process.geteuid?.()
|
20
21
|
const sockPath = `/run/user/${uid}/highstate.sock`
|
21
22
|
|
23
|
+
try {
|
24
|
+
await rm(sockPath, { force: true })
|
25
|
+
} catch (error) {
|
26
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
|
27
|
+
services.logger.error({ error }, "failed to remove existing socket file")
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
22
31
|
await server.listen(`unix:${sockPath}`)
|
23
32
|
|
24
33
|
services.workerManager.config.HIGHSTATE_WORKER_API_PATH = sockPath
|
@@ -1,11 +1,10 @@
|
|
1
|
-
import type { Services } from "@highstate/backend"
|
2
|
-
import type
|
3
|
-
import { ServerError, Status, type CallContext } from "nice-grpc-common"
|
1
|
+
import type { ApiKey, Services } from "@highstate/backend"
|
2
|
+
import { type CallContext, ServerError, Status } from "nice-grpc-common"
|
4
3
|
|
5
4
|
export async function authenticate(
|
6
5
|
services: Services,
|
7
6
|
context: CallContext,
|
8
|
-
): Promise<[projectId: string, apiKey:
|
7
|
+
): Promise<[projectId: string, apiKey: ApiKey]> {
|
9
8
|
const token = context.metadata.get("api-key")
|
10
9
|
if (!token) {
|
11
10
|
throw new ServerError(Status.UNAUTHENTICATED, "No API key provided")
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import type { Services } from "@highstate/backend"
|
2
|
-
import { ServerError, Status, type CallContext, type ServerMiddlewareCall } from "nice-grpc-common"
|
3
|
-
import { isAbortError } from "abort-controller-x"
|
4
2
|
import { AccessError } from "@highstate/backend/shared"
|
3
|
+
import { isAbortError } from "abort-controller-x"
|
4
|
+
import { type CallContext, ServerError, type ServerMiddlewareCall, Status } from "nice-grpc-common"
|
5
5
|
|
6
6
|
export function createErrorHandlingMiddleware(services: Services) {
|
7
7
|
return async function* errorHandlingMiddleware<TRequest, TResponse>(
|