@highstate/backend-api 0.9.20 → 0.9.21
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 +8 -9
- package/dist/index.js.map +1 -1
- package/package.json +14 -7
- package/src/handlers/secret.ts +6 -8
- package/src/index.ts +3 -3
package/dist/index.js
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
import { rm } from 'node:fs/promises';
|
2
|
-
import { createServer } from 'nice-grpc';
|
3
2
|
import { InstanceServiceDefinition } from '@highstate/api/instance.v1';
|
4
3
|
import { SecretServiceDefinition } from '@highstate/api/secret.v1';
|
5
4
|
import { WorkerServiceDefinition } from '@highstate/api/worker.v1';
|
6
|
-
import {
|
5
|
+
import { createServer } from 'nice-grpc';
|
7
6
|
import { AccessError, instanceCustomStatusInputSchema } from '@highstate/backend/shared';
|
8
|
-
import { isAbortError } from 'abort-controller-x';
|
9
7
|
import { z, commonObjectMetaSchema } from '@highstate/contract';
|
8
|
+
import { ServerError, Status } from 'nice-grpc-common';
|
9
|
+
import { isAbortError } from 'abort-controller-x';
|
10
10
|
|
11
11
|
// src/index.ts
|
12
12
|
async function authenticate(services, context) {
|
@@ -48,6 +48,8 @@ function parseArgument(request, argumentName, schema) {
|
|
48
48
|
}
|
49
49
|
return result.data;
|
50
50
|
}
|
51
|
+
|
52
|
+
// src/handlers/instance.ts
|
51
53
|
function createInstanceService(services) {
|
52
54
|
return {
|
53
55
|
async updateCustomStatus(request, context) {
|
@@ -79,12 +81,9 @@ function createInstanceService(services) {
|
|
79
81
|
// src/handlers/secret.ts
|
80
82
|
function createSecretService(services) {
|
81
83
|
return {
|
82
|
-
async getSecretContent(
|
83
|
-
const [
|
84
|
-
|
85
|
-
return {
|
86
|
-
content
|
87
|
-
};
|
84
|
+
async getSecretContent(_request, context) {
|
85
|
+
const [_projectId] = await authenticate(services, context);
|
86
|
+
throw new Error("Not implemented");
|
88
87
|
}
|
89
88
|
};
|
90
89
|
}
|
package/dist/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/shared/authentication.ts","../src/shared/error-handling.ts","../src/shared/validation.ts","../src/handlers/instance.ts","../src/handlers/secret.ts","../src/handlers/worker.ts","../src/index.ts"],"names":["ServerError","Status","z"],"mappings":"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","file":"index.js","sourcesContent":["import type { ApiKey, Services } from \"@highstate/backend\"\nimport { type CallContext, ServerError, Status } from \"nice-grpc-common\"\n\nexport async function authenticate(\n services: Services,\n context: CallContext,\n): Promise<[projectId: string, apiKey: ApiKey]> {\n const token = context.metadata.get(\"api-key\")\n if (!token) {\n throw new ServerError(Status.UNAUTHENTICATED, \"No API key provided\")\n }\n\n const projectId = context.metadata.get(\"project-id\")\n if (!projectId) {\n throw new ServerError(Status.UNAUTHENTICATED, \"No project ID provided\")\n }\n\n const apiKey = await services.apiKeyService.getApiKeyByToken(projectId, token)\n\n return [projectId, apiKey]\n}\n","import type { Services } from \"@highstate/backend\"\nimport { AccessError } from \"@highstate/backend/shared\"\nimport { isAbortError } from \"abort-controller-x\"\nimport { type CallContext, ServerError, type ServerMiddlewareCall, Status } from \"nice-grpc-common\"\n\nexport function createErrorHandlingMiddleware(services: Services) {\n return async function* errorHandlingMiddleware<TRequest, TResponse>(\n call: ServerMiddlewareCall<TRequest, TResponse>,\n context: CallContext,\n ) {\n try {\n return yield* call.next(call.request, context)\n } catch (error) {\n if (error instanceof ServerError || isAbortError(error)) {\n throw error\n }\n\n if (error instanceof AccessError) {\n services.logger.info({ error }, \"access denied\")\n throw new ServerError(Status.UNAUTHENTICATED, \"Access denied\")\n }\n\n services.logger.error({ error }, \"unexpected error\")\n throw new ServerError(Status.INTERNAL, \"An unexpected error occurred\")\n }\n }\n}\n","import type { z } from \"zod\"\nimport { ServerError, Status } from \"nice-grpc-common\"\n\nexport function parseArgument<\n TRequest,\n TArgumentName extends string & keyof TRequest,\n TSchema extends z.ZodType,\n>(request: TRequest, argumentName: TArgumentName, schema: TSchema): z.infer<TSchema> {\n const result = schema.safeParse(request[argumentName])\n if (!result.success) {\n throw new ServerError(\n Status.INVALID_ARGUMENT,\n `Invalid argument \"${argumentName}\": ${result.error.message}`,\n )\n }\n\n return result.data\n}\n","import type { InstanceServiceImplementation } from \"@highstate/api/instance.v1\"\nimport type { Services } from \"@highstate/backend\"\nimport { instanceCustomStatusInputSchema } from \"@highstate/backend/shared\"\nimport { z } from \"@highstate/contract\"\nimport { authenticate, parseArgument } from \"../shared\"\n\nexport function createInstanceService(services: Services): InstanceServiceImplementation {\n return {\n async updateCustomStatus(request, context) {\n const [projectId, apiKey] = await authenticate(services, context)\n\n // TODO: validate instance access\n\n const stateId = parseArgument(request, \"stateId\", z.cuid2())\n const customStatus = parseArgument(request, \"status\", instanceCustomStatusInputSchema)\n\n await services.instanceStateService.updateCustomStatus(\n projectId,\n stateId,\n apiKey.serviceAccountId,\n customStatus,\n )\n\n return {}\n },\n\n async removeCustomStatus(request, context) {\n const [projectId, apiKey] = await authenticate(services, context)\n\n // TODO: validate instance access\n\n const stateId = parseArgument(request, \"stateId\", z.cuid2())\n\n await services.instanceStateService.removeCustomStatus(\n projectId,\n stateId,\n apiKey.serviceAccountId,\n request.statusName,\n )\n\n return {}\n },\n }\n}\n","import type { SecretServiceImplementation } from \"@highstate/api/secret.v1\"\nimport type { Services } from \"@highstate/backend\"\nimport { authenticate } from \"../shared\"\n\nexport function createSecretService(services: Services): SecretServiceImplementation {\n return {\n async getSecretContent(request, context) {\n const [projectId] = await authenticate(services, context)\n\n // TODO: validate secret access\n\n const content = await services.stateManager\n .getSecretContentRepository(projectId)\n .get(request.secretId)\n\n return {\n content,\n }\n },\n }\n}\n","import type { WorkerServiceImplementation } from \"@highstate/api/worker.v1\"\nimport type { Services } from \"@highstate/backend\"\nimport { commonObjectMetaSchema, z } from \"@highstate/contract\"\nimport { authenticate, parseArgument } from \"../shared\"\n\nexport function createWorkerService(services: Services): WorkerServiceImplementation {\n return {\n async *connect(request, context) {\n const [projectId] = await authenticate(services, context)\n\n const workerVersionId = parseArgument(request, \"workerVersionId\", z.cuid2())\n\n // set worker as running\n services.workerManager.setWorkerRunning(projectId, workerVersionId)\n\n // get existing registrations for this worker version\n const database = await services.database.forProject(projectId)\n const existingRegistrations = await database.workerUnitRegistration.findMany({\n where: { workerVersionId },\n select: { stateId: true, params: true },\n })\n\n // emit existing registrations\n for (const registration of existingRegistrations) {\n yield {\n event: {\n $case: \"unitRegistration\",\n value: {\n stateId: registration.stateId,\n params: registration.params,\n },\n },\n }\n }\n\n // subscribe to new registration events for this worker version\n const registrationStream = await services.pubsubManager.subscribe([\n \"worker-unit-registration\",\n projectId,\n workerVersionId,\n ])\n\n // emit new registration/deregistration events\n for await (const event of registrationStream) {\n if (event.type === \"registered\") {\n yield {\n event: {\n $case: \"unitRegistration\",\n value: {\n instanceId: event.instanceId,\n params: event.params,\n },\n },\n }\n } else if (event.type === \"deregistered\") {\n yield {\n event: {\n $case: \"unitDeregistration\",\n value: {\n instanceId: event.instanceId,\n },\n },\n }\n }\n }\n },\n\n async updateWorkerVersionMeta(request, context) {\n const [projectId] = await authenticate(services, context)\n\n const workerVersionId = parseArgument(request, \"workerVersionId\", z.string())\n const meta = parseArgument(request, \"meta\", commonObjectMetaSchema)\n\n await services.workerService.updateWorkerVersionMeta(projectId, workerVersionId, meta)\n\n return {}\n },\n }\n}\n","import { rm } from \"node:fs/promises\"\nimport { createServer } from \"nice-grpc\"\nimport type { Services } from \"@highstate/backend\"\nimport { InstanceServiceDefinition } from \"@highstate/api/instance.v1\"\nimport { SecretServiceDefinition } from \"@highstate/api/secret.v1\"\nimport { WorkerServiceDefinition } from \"@highstate/api/worker.v1\"\nimport { createErrorHandlingMiddleware } from \"./shared\"\nimport { createInstanceService } from \"./handlers/instance\"\nimport { createSecretService } from \"./handlers/secret\"\nimport { createWorkerService } from \"./handlers/worker\"\n\nexport async function startBackedApi(services: Services) {\n const server = createServer()\n server.use(createErrorHandlingMiddleware(services))\n\n server.add(InstanceServiceDefinition, createInstanceService(services))\n server.add(SecretServiceDefinition, createSecretService(services))\n server.add(WorkerServiceDefinition, createWorkerService(services))\n\n const uid = process.geteuid?.()\n const sockPath = `/run/user/${uid}/highstate.sock`\n\n try {\n await rm(sockPath, { force: true })\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) {\n services.logger.error({ error }, \"failed to remove existing socket file\")\n }\n }\n\n await server.listen(`unix:${sockPath}`)\n\n services.workerManager.config.HIGHSTATE_WORKER_API_PATH = sockPath\n services.logger.info(`api listening at %s`, sockPath)\n}\n"]}
|
1
|
+
{"version":3,"sources":["../src/shared/authentication.ts","../src/shared/error-handling.ts","../src/shared/validation.ts","../src/handlers/instance.ts","../src/handlers/secret.ts","../src/handlers/worker.ts","../src/index.ts"],"names":["ServerError","Status","z"],"mappings":"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","file":"index.js","sourcesContent":["import type { ApiKey, Services } from \"@highstate/backend\"\nimport { type CallContext, ServerError, Status } from \"nice-grpc-common\"\n\nexport async function authenticate(\n services: Services,\n context: CallContext,\n): Promise<[projectId: string, apiKey: ApiKey]> {\n const token = context.metadata.get(\"api-key\")\n if (!token) {\n throw new ServerError(Status.UNAUTHENTICATED, \"No API key provided\")\n }\n\n const projectId = context.metadata.get(\"project-id\")\n if (!projectId) {\n throw new ServerError(Status.UNAUTHENTICATED, \"No project ID provided\")\n }\n\n const apiKey = await services.apiKeyService.getApiKeyByToken(projectId, token)\n\n return [projectId, apiKey]\n}\n","import type { Services } from \"@highstate/backend\"\nimport { AccessError } from \"@highstate/backend/shared\"\nimport { isAbortError } from \"abort-controller-x\"\nimport { type CallContext, ServerError, type ServerMiddlewareCall, Status } from \"nice-grpc-common\"\n\nexport function createErrorHandlingMiddleware(services: Services) {\n return async function* errorHandlingMiddleware<TRequest, TResponse>(\n call: ServerMiddlewareCall<TRequest, TResponse>,\n context: CallContext,\n ) {\n try {\n return yield* call.next(call.request, context)\n } catch (error) {\n if (error instanceof ServerError || isAbortError(error)) {\n throw error\n }\n\n if (error instanceof AccessError) {\n services.logger.info({ error }, \"access denied\")\n throw new ServerError(Status.UNAUTHENTICATED, \"Access denied\")\n }\n\n services.logger.error({ error }, \"unexpected error\")\n throw new ServerError(Status.INTERNAL, \"An unexpected error occurred\")\n }\n }\n}\n","import type { z } from \"zod\"\nimport { ServerError, Status } from \"nice-grpc-common\"\n\nexport function parseArgument<\n TRequest,\n TArgumentName extends string & keyof TRequest,\n TSchema extends z.ZodType,\n>(request: TRequest, argumentName: TArgumentName, schema: TSchema): z.infer<TSchema> {\n const result = schema.safeParse(request[argumentName])\n if (!result.success) {\n throw new ServerError(\n Status.INVALID_ARGUMENT,\n `Invalid argument \"${argumentName}\": ${result.error.message}`,\n )\n }\n\n return result.data\n}\n","import type { InstanceServiceImplementation } from \"@highstate/api/instance.v1\"\nimport type { Services } from \"@highstate/backend\"\nimport { instanceCustomStatusInputSchema } from \"@highstate/backend/shared\"\nimport { z } from \"@highstate/contract\"\nimport { authenticate, parseArgument } from \"../shared\"\n\nexport function createInstanceService(services: Services): InstanceServiceImplementation {\n return {\n async updateCustomStatus(request, context) {\n const [projectId, apiKey] = await authenticate(services, context)\n\n // TODO: validate instance access\n\n const stateId = parseArgument(request, \"stateId\", z.cuid2())\n const customStatus = parseArgument(request, \"status\", instanceCustomStatusInputSchema)\n\n await services.instanceStateService.updateCustomStatus(\n projectId,\n stateId,\n apiKey.serviceAccountId,\n customStatus,\n )\n\n return {}\n },\n\n async removeCustomStatus(request, context) {\n const [projectId, apiKey] = await authenticate(services, context)\n\n // TODO: validate instance access\n\n const stateId = parseArgument(request, \"stateId\", z.cuid2())\n\n await services.instanceStateService.removeCustomStatus(\n projectId,\n stateId,\n apiKey.serviceAccountId,\n request.statusName,\n )\n\n return {}\n },\n }\n}\n","import type { SecretServiceImplementation } from \"@highstate/api/secret.v1\"\nimport type { Services } from \"@highstate/backend\"\nimport { authenticate } from \"../shared\"\n\nexport function createSecretService(services: Services): SecretServiceImplementation {\n return {\n async getSecretContent(_request, context) {\n const [_projectId] = await authenticate(services, context)\n\n // TODO: validate secret access\n\n throw new Error(\"Not implemented\")\n\n // return {\n // content,\n // }\n },\n }\n}\n","import type { WorkerServiceImplementation } from \"@highstate/api/worker.v1\"\nimport type { Services } from \"@highstate/backend\"\nimport { commonObjectMetaSchema, z } from \"@highstate/contract\"\nimport { authenticate, parseArgument } from \"../shared\"\n\nexport function createWorkerService(services: Services): WorkerServiceImplementation {\n return {\n async *connect(request, context) {\n const [projectId] = await authenticate(services, context)\n\n const workerVersionId = parseArgument(request, \"workerVersionId\", z.cuid2())\n\n // set worker as running\n services.workerManager.setWorkerRunning(projectId, workerVersionId)\n\n // get existing registrations for this worker version\n const database = await services.database.forProject(projectId)\n const existingRegistrations = await database.workerUnitRegistration.findMany({\n where: { workerVersionId },\n select: { stateId: true, params: true },\n })\n\n // emit existing registrations\n for (const registration of existingRegistrations) {\n yield {\n event: {\n $case: \"unitRegistration\",\n value: {\n stateId: registration.stateId,\n params: registration.params,\n },\n },\n }\n }\n\n // subscribe to new registration events for this worker version\n const registrationStream = await services.pubsubManager.subscribe([\n \"worker-unit-registration\",\n projectId,\n workerVersionId,\n ])\n\n // emit new registration/deregistration events\n for await (const event of registrationStream) {\n if (event.type === \"registered\") {\n yield {\n event: {\n $case: \"unitRegistration\",\n value: {\n instanceId: event.instanceId,\n params: event.params,\n },\n },\n }\n } else if (event.type === \"deregistered\") {\n yield {\n event: {\n $case: \"unitDeregistration\",\n value: {\n instanceId: event.instanceId,\n },\n },\n }\n }\n }\n },\n\n async updateWorkerVersionMeta(request, context) {\n const [projectId] = await authenticate(services, context)\n\n const workerVersionId = parseArgument(request, \"workerVersionId\", z.string())\n const meta = parseArgument(request, \"meta\", commonObjectMetaSchema)\n\n await services.workerService.updateWorkerVersionMeta(projectId, workerVersionId, meta)\n\n return {}\n },\n }\n}\n","import type { Services } from \"@highstate/backend\"\nimport { rm } from \"node:fs/promises\"\nimport { InstanceServiceDefinition } from \"@highstate/api/instance.v1\"\nimport { SecretServiceDefinition } from \"@highstate/api/secret.v1\"\nimport { WorkerServiceDefinition } from \"@highstate/api/worker.v1\"\nimport { createServer } from \"nice-grpc\"\nimport { createInstanceService } from \"./handlers/instance\"\nimport { createSecretService } from \"./handlers/secret\"\nimport { createWorkerService } from \"./handlers/worker\"\nimport { createErrorHandlingMiddleware } from \"./shared\"\n\nexport async function startBackedApi(services: Services) {\n const server = createServer()\n server.use(createErrorHandlingMiddleware(services))\n\n server.add(InstanceServiceDefinition, createInstanceService(services))\n server.add(SecretServiceDefinition, createSecretService(services))\n server.add(WorkerServiceDefinition, createWorkerService(services))\n\n const uid = process.geteuid?.()\n const sockPath = `/run/user/${uid}/highstate.sock`\n\n try {\n await rm(sockPath, { force: true })\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) {\n services.logger.error({ error }, \"failed to remove existing socket file\")\n }\n }\n\n await server.listen(`unix:${sockPath}`)\n\n services.workerManager.config.HIGHSTATE_WORKER_API_PATH = sockPath\n services.logger.info(`api listening at %s`, sockPath)\n}\n"]}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@highstate/backend-api",
|
3
|
-
"version": "0.9.
|
3
|
+
"version": "0.9.21",
|
4
4
|
"type": "module",
|
5
5
|
"files": [
|
6
6
|
"dist",
|
@@ -17,7 +17,10 @@
|
|
17
17
|
"access": "public"
|
18
18
|
},
|
19
19
|
"scripts": {
|
20
|
-
"build": "highstate build"
|
20
|
+
"build": "highstate build",
|
21
|
+
"typecheck": "tsgo --noEmit --skipLibCheck",
|
22
|
+
"biome": "biome check --write --unsafe --error-on-warnings",
|
23
|
+
"biome:check": "biome check --error-on-warnings"
|
21
24
|
},
|
22
25
|
"peerDependencies": {
|
23
26
|
"@pulumi/pulumi": "^3.163.0",
|
@@ -33,14 +36,18 @@
|
|
33
36
|
},
|
34
37
|
"dependencies": {
|
35
38
|
"@bufbuild/protobuf": "^2.6.1",
|
36
|
-
"@highstate/api": "^0.9.
|
37
|
-
"@highstate/backend": "^0.9.
|
38
|
-
"@highstate/cli": "^0.9.
|
39
|
-
"@highstate/contract": "^0.9.
|
39
|
+
"@highstate/api": "^0.9.21",
|
40
|
+
"@highstate/backend": "^0.9.21",
|
41
|
+
"@highstate/cli": "^0.9.21",
|
42
|
+
"@highstate/contract": "^0.9.21",
|
40
43
|
"abort-controller-x": "^0.4.3",
|
41
44
|
"nice-grpc": "^2.1.12",
|
42
45
|
"nice-grpc-common": "^2.0.2",
|
43
46
|
"zod": "^4.0.5"
|
44
47
|
},
|
45
|
-
"
|
48
|
+
"devDependencies": {
|
49
|
+
"@biomejs/biome": "2.2.0",
|
50
|
+
"@typescript/native-preview": "^7.0.0-dev.20250920.1"
|
51
|
+
},
|
52
|
+
"gitHead": "390ff15c0e0076822a682f9d4e19260942a8d6c2"
|
46
53
|
}
|
package/src/handlers/secret.ts
CHANGED
@@ -4,18 +4,16 @@ import { authenticate } from "../shared"
|
|
4
4
|
|
5
5
|
export function createSecretService(services: Services): SecretServiceImplementation {
|
6
6
|
return {
|
7
|
-
async getSecretContent(
|
8
|
-
const [
|
7
|
+
async getSecretContent(_request, context) {
|
8
|
+
const [_projectId] = await authenticate(services, context)
|
9
9
|
|
10
10
|
// TODO: validate secret access
|
11
11
|
|
12
|
-
|
13
|
-
.getSecretContentRepository(projectId)
|
14
|
-
.get(request.secretId)
|
12
|
+
throw new Error("Not implemented")
|
15
13
|
|
16
|
-
return {
|
17
|
-
|
18
|
-
}
|
14
|
+
// return {
|
15
|
+
// content,
|
16
|
+
// }
|
19
17
|
},
|
20
18
|
}
|
21
19
|
}
|
package/src/index.ts
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
import { rm } from "node:fs/promises"
|
2
|
-
import { createServer } from "nice-grpc"
|
3
1
|
import type { Services } from "@highstate/backend"
|
2
|
+
import { rm } from "node:fs/promises"
|
4
3
|
import { InstanceServiceDefinition } from "@highstate/api/instance.v1"
|
5
4
|
import { SecretServiceDefinition } from "@highstate/api/secret.v1"
|
6
5
|
import { WorkerServiceDefinition } from "@highstate/api/worker.v1"
|
7
|
-
import {
|
6
|
+
import { createServer } from "nice-grpc"
|
8
7
|
import { createInstanceService } from "./handlers/instance"
|
9
8
|
import { createSecretService } from "./handlers/secret"
|
10
9
|
import { createWorkerService } from "./handlers/worker"
|
10
|
+
import { createErrorHandlingMiddleware } from "./shared"
|
11
11
|
|
12
12
|
export async function startBackedApi(services: Services) {
|
13
13
|
const server = createServer()
|