c8y-nitro 0.3.0 → 0.4.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.
Files changed (51) hide show
  1. package/dist/{cli/commands/bootstrap.mjs → bootstrap-BqWPkH8q.mjs} +5 -8
  2. package/dist/{cli/utils/c8y-api.mjs → c8y-api-BBSKRwKs.mjs} +73 -3
  3. package/dist/cli/index.mjs +5 -7
  4. package/dist/{cli/utils/config.mjs → config-Dqi-ttQi.mjs} +1 -3
  5. package/dist/{cli/utils/env-file.mjs → env-file-B0BK-uZW.mjs} +1 -3
  6. package/dist/{types/manifest.d.mts → index-CzUqbp5C.d.mts} +94 -1
  7. package/dist/index.d.mts +1 -1
  8. package/dist/index.mjs +549 -14
  9. package/dist/{cli/commands/options.mjs → options-BDDJWdph.mjs} +3 -6
  10. package/dist/{package.mjs → package-CobwNpP9.mjs} +2 -3
  11. package/dist/{cli/commands/roles.mjs → roles-DJxp2d8p.mjs} +3 -6
  12. package/dist/runtime/handlers/liveness-readiness.mjs +7 -0
  13. package/dist/runtime/middlewares/dev-user.mjs +23 -0
  14. package/dist/runtime/plugins/c8y-variables.mjs +17 -0
  15. package/dist/runtime/plugins/enrich-logs.mjs +15 -0
  16. package/dist/types.d.mts +2 -25
  17. package/dist/types.mjs +1 -1
  18. package/dist/utils.d.mts +292 -6
  19. package/dist/utils.mjs +444 -8
  20. package/package.json +12 -11
  21. package/dist/module/apiClient.mjs +0 -207
  22. package/dist/module/autoBootstrap.mjs +0 -54
  23. package/dist/module/c8yzip.mjs +0 -66
  24. package/dist/module/constants.mjs +0 -6
  25. package/dist/module/docker.mjs +0 -101
  26. package/dist/module/manifest.mjs +0 -72
  27. package/dist/module/probeCheck.mjs +0 -30
  28. package/dist/module/register.mjs +0 -58
  29. package/dist/module/runtime/handlers/liveness-readiness.ts +0 -7
  30. package/dist/module/runtime/middlewares/dev-user.ts +0 -25
  31. package/dist/module/runtime/plugins/c8y-variables.ts +0 -24
  32. package/dist/module/runtime.mjs +0 -38
  33. package/dist/module/runtimeConfig.mjs +0 -20
  34. package/dist/types/apiClient.d.mts +0 -16
  35. package/dist/types/cache.d.mts +0 -28
  36. package/dist/types/roles.d.mts +0 -4
  37. package/dist/types/tenantOptions.d.mts +0 -13
  38. package/dist/types/zip.d.mts +0 -22
  39. package/dist/utils/client.d.mts +0 -52
  40. package/dist/utils/client.mjs +0 -90
  41. package/dist/utils/credentials.d.mts +0 -71
  42. package/dist/utils/credentials.mjs +0 -120
  43. package/dist/utils/internal/common.mjs +0 -26
  44. package/dist/utils/logging.d.mts +0 -3
  45. package/dist/utils/logging.mjs +0 -4
  46. package/dist/utils/middleware.d.mts +0 -89
  47. package/dist/utils/middleware.mjs +0 -62
  48. package/dist/utils/resources.d.mts +0 -30
  49. package/dist/utils/resources.mjs +0 -49
  50. package/dist/utils/tenantOptions.d.mts +0 -65
  51. package/dist/utils/tenantOptions.mjs +0 -127
@@ -1,62 +0,0 @@
1
- import { useUserClient } from "./client.mjs";
2
- import { useUserRoles } from "./resources.mjs";
3
- import process from "node:process";
4
- import { HTTPError, defineHandler } from "nitro/h3";
5
-
6
- //#region src/utils/middleware.ts
7
- function hasUserRequiredRole(roleOrRoles) {
8
- return defineHandler(async (event) => {
9
- const requiredRoles = Array.isArray(roleOrRoles) ? roleOrRoles : [roleOrRoles];
10
- const userRoles = await useUserRoles(event);
11
- if (!requiredRoles.some((role) => userRoles.includes(role))) throw new HTTPError({
12
- status: 403,
13
- statusText: "Forbidden",
14
- message: `User does not have required role(s) to access this resource: ${requiredRoles.join(", ")}`
15
- });
16
- });
17
- }
18
- function isUserFromAllowedTenant(tenantIdOrIds) {
19
- return defineHandler(async (event) => {
20
- const allowedTenants = Array.isArray(tenantIdOrIds) ? tenantIdOrIds : [tenantIdOrIds];
21
- const userTenantId = useUserClient(event).core.tenant;
22
- if (!allowedTenants.includes(userTenantId)) throw new HTTPError({
23
- status: 403,
24
- statusText: "Forbidden",
25
- message: `User's tenant '${userTenantId}' is not allowed to access this resource. Allowed tenants: ${allowedTenants.join(", ")}`
26
- });
27
- });
28
- }
29
- /**
30
- * Middleware to check if the current user belongs to the deployed tenant.\
31
- * The deployed tenant is where this microservice is hosted (C8Y_BOOTSTRAP_TENANT).\
32
- * If the user's tenant doesn't match the deployed tenant, throws a 403 Forbidden error.\
33
- * Must be used within a request handler context.\
34
- * @returns Event handler that validates user is from deployed tenant
35
- * @example
36
- * // Only allow users from the deployed tenant:
37
- * export default defineHandler({
38
- * middleware: [isUserFromDeployedTenant()],
39
- * handler: async () => {
40
- * return { message: 'You have access' }
41
- * }
42
- * })
43
- */
44
- function isUserFromDeployedTenant() {
45
- return defineHandler(async (event) => {
46
- const userTenantId = useUserClient(event).core.tenant;
47
- const deployedTenantId = process.env.C8Y_BOOTSTRAP_TENANT;
48
- if (!deployedTenantId) throw new HTTPError({
49
- status: 500,
50
- statusText: "Internal Server Error",
51
- message: "C8Y_BOOTSTRAP_TENANT environment variable is not set"
52
- });
53
- if (userTenantId !== deployedTenantId) throw new HTTPError({
54
- status: 403,
55
- statusText: "Forbidden",
56
- message: `Only users from tenant '${deployedTenantId}' can access this resource.`
57
- });
58
- });
59
- }
60
-
61
- //#endregion
62
- export { hasUserRequiredRole, isUserFromAllowedTenant, isUserFromDeployedTenant };
@@ -1,30 +0,0 @@
1
- import { ICurrentUser } from "@c8y/client";
2
- import { H3Event } from "nitro/h3";
3
- import { ServerRequest } from "nitro/types";
4
-
5
- //#region src/utils/resources.d.ts
6
- /**
7
- * Fetches the current user from Cumulocity using credentials extracted from the current request's headers.
8
- * This is a non-cached version - fetches fresh data on every call.
9
- * @param requestOrEvent - The H3Event or ServerRequest from the current request
10
- * @returns The current user object from Cumulocity
11
- * @example
12
- * // In a request handler:
13
- * const user = await useUser(event)
14
- * console.log(user.userName, user.email)
15
- */
16
- declare function useUser(requestOrEvent: ServerRequest | H3Event): Promise<ICurrentUser>;
17
- /**
18
- * Fetches the roles of the current user from Cumulocity.
19
- * Internally calls `useUser()` and extracts role IDs from the user object.
20
- * This is a non-cached version - fetches fresh data on every call.
21
- * @param requestOrEvent - The H3Event or ServerRequest from the current request
22
- * @returns Array of role ID strings assigned to the current user
23
- * @example
24
- * // In a request handler:
25
- * const roles = await useUserRoles(event)
26
- * console.log(roles) // ['ROLE_INVENTORY_READ', 'ROLE_INVENTORY_ADMIN']
27
- */
28
- declare function useUserRoles(requestOrEvent: ServerRequest | H3Event): Promise<string[]>;
29
- //#endregion
30
- export { useUser, useUserRoles };
@@ -1,49 +0,0 @@
1
- import { useUserClient } from "./client.mjs";
2
- import { HTTPError } from "nitro/h3";
3
-
4
- //#region src/utils/resources.ts
5
- /**
6
- * Fetches the current user from Cumulocity using credentials extracted from the current request's headers.
7
- * This is a non-cached version - fetches fresh data on every call.
8
- * @param requestOrEvent - The H3Event or ServerRequest from the current request
9
- * @returns The current user object from Cumulocity
10
- * @example
11
- * // In a request handler:
12
- * const user = await useUser(event)
13
- * console.log(user.userName, user.email)
14
- */
15
- async function useUser(requestOrEvent) {
16
- const request = "req" in requestOrEvent ? requestOrEvent.req : requestOrEvent;
17
- if (request.context?.["c8y_user"]) return request.context["c8y_user"];
18
- const { res, data: user } = await useUserClient(requestOrEvent).user.currentWithEffectiveRoles();
19
- if (!res.ok) throw new HTTPError({
20
- message: `Failed to fetch current user`,
21
- status: res.status,
22
- statusText: res.statusText
23
- });
24
- request.context ??= {};
25
- request.context["c8y_user"] = user;
26
- return user;
27
- }
28
- /**
29
- * Fetches the roles of the current user from Cumulocity.
30
- * Internally calls `useUser()` and extracts role IDs from the user object.
31
- * This is a non-cached version - fetches fresh data on every call.
32
- * @param requestOrEvent - The H3Event or ServerRequest from the current request
33
- * @returns Array of role ID strings assigned to the current user
34
- * @example
35
- * // In a request handler:
36
- * const roles = await useUserRoles(event)
37
- * console.log(roles) // ['ROLE_INVENTORY_READ', 'ROLE_INVENTORY_ADMIN']
38
- */
39
- async function useUserRoles(requestOrEvent) {
40
- const request = "req" in requestOrEvent ? requestOrEvent.req : requestOrEvent;
41
- if (request.context?.["c8y_user_roles"]) return request.context["c8y_user_roles"];
42
- const userRoles = (await useUser(requestOrEvent)).effectiveRoles?.map((role) => role.name) ?? [];
43
- request.context ??= {};
44
- request.context["c8y_user_roles"] = userRoles;
45
- return userRoles;
46
- }
47
-
48
- //#endregion
49
- export { useUser, useUserRoles };
@@ -1,65 +0,0 @@
1
- import { C8YTenantOptionKey } from "c8y-nitro/types";
2
-
3
- //#region src/utils/tenantOptions.d.ts
4
- /**
5
- * Fetches a tenant option value by key.\
6
- * Uses the deployed tenant's service user credentials to access the Options API.\
7
- * Results are cached based on the configured TTL (default: 10 minutes).
8
- *
9
- * @param key - The tenant option key to fetch (as defined in `manifest.settings`)
10
- * @returns The option value as a string
11
- *
12
- * @config Cache TTL can be configured via:
13
- * - `c8y.cache.defaultTenantOptionsTTL` — Default TTL for all keys (in seconds)
14
- * - `c8y.cache.tenantOptions` — Per-key TTL overrides
15
- * - `NITRO_C8Y_DEFAULT_TENANT_OPTIONS_TTL` — Environment variable for default TTL
16
- *
17
- * @note For encrypted options (keys starting with `credentials.`), the value is automatically
18
- * decrypted by Cumulocity if this microservice is the owner of the option (category matches
19
- * the microservice's settingsCategory/contextPath/name). The `credentials.` prefix is
20
- * automatically stripped when calling the API.
21
- *
22
- * @example
23
- * // Fetch a tenant option:
24
- * const value = await useTenantOption('myOption')
25
- *
26
- * // Fetch an encrypted secret:
27
- * const secret = await useTenantOption('credentials.apiKey')
28
- *
29
- * // Invalidate cache for a specific key:
30
- * await useTenantOption.invalidate('myOption')
31
- *
32
- * // Force refresh a specific key:
33
- * const fresh = await useTenantOption.refresh('myOption')
34
- *
35
- * // Invalidate all tenant option caches:
36
- * await useTenantOption.invalidateAll()
37
- *
38
- * // Refresh all tenant options:
39
- * const all = await useTenantOption.refreshAll()
40
- */
41
- declare const useTenantOption: ((key: C8YTenantOptionKey) => Promise<string | undefined>) & {
42
- /**
43
- * Invalidate the cache for a specific tenant option key.
44
- * @param key - The tenant option key to invalidate
45
- */
46
- invalidate: (key: C8YTenantOptionKey) => Promise<void>;
47
- /**
48
- * Force refresh a specific tenant option key (invalidates and re-fetches).
49
- * @param key - The tenant option key to refresh
50
- */
51
- refresh: (key: C8YTenantOptionKey) => Promise<string | undefined>;
52
- /**
53
- * Invalidate all tenant option caches that have been accessed.
54
- * Only invalidates keys that have been fetched at least once.
55
- */
56
- invalidateAll: () => Promise<void>;
57
- /**
58
- * Refresh all tenant options that have been accessed.
59
- * Only refreshes keys that have been fetched at least once.
60
- * @returns Object mapping keys to their refreshed values
61
- */
62
- refreshAll: () => Promise<Record<string, string | undefined>>;
63
- };
64
- //#endregion
65
- export { useTenantOption };
@@ -1,127 +0,0 @@
1
- import { useDeployedTenantClient } from "./client.mjs";
2
- import { defineCachedFunction } from "nitro/cache";
3
- import { useStorage } from "nitro/storage";
4
- import { useRuntimeConfig } from "nitro/runtime-config";
5
-
6
- //#region src/utils/tenantOptions.ts
7
- /**
8
- * Gets the cache TTL for a specific tenant option key.
9
- * Uses per-key override if defined, otherwise falls back to default TTL.
10
- * @param key - The tenant option key
11
- */
12
- function getTenantOptionCacheTTL(key) {
13
- const config = useRuntimeConfig();
14
- return config.c8yTenantOptionsPerKeyTTL?.[key] ?? config.c8yDefaultTenantOptionsTTL ?? 600;
15
- }
16
- /**
17
- * Internal storage for cached functions per key
18
- */
19
- const tenantOptionFetchers = {};
20
- /**
21
- * Factory function that creates a cached fetcher for a specific tenant option key.
22
- * @param key - The tenant option key
23
- */
24
- function createCachedTenantOptionFetcher(key) {
25
- const cacheName = `_c8y_nitro_tenant_option_${key.replace(/\./g, "_")}`;
26
- const fetcher = defineCachedFunction(async () => {
27
- const client = await useDeployedTenantClient();
28
- const category = useRuntimeConfig().c8ySettingsCategory;
29
- const apiKey = key.replace(/^credentials\./, "");
30
- try {
31
- return (await client.options.tenant.detail({
32
- key: apiKey,
33
- category
34
- })).data.value;
35
- } catch (error) {
36
- if (error?.res?.status === 404 || error?.status === 404) return;
37
- throw error;
38
- }
39
- }, {
40
- maxAge: getTenantOptionCacheTTL(key),
41
- name: cacheName,
42
- group: "c8y_nitro",
43
- swr: false
44
- });
45
- return Object.assign(fetcher, {
46
- invalidate: async () => {
47
- const completeKey = `c8y_nitro:functions:${cacheName}.json`;
48
- await useStorage("cache").removeItem(completeKey);
49
- },
50
- refresh: async () => {
51
- const completeKey = `c8y_nitro:functions:${cacheName}.json`;
52
- await useStorage("cache").removeItem(completeKey);
53
- return await fetcher();
54
- }
55
- });
56
- }
57
- /**
58
- * Gets or creates a cached fetcher for a specific tenant option key.
59
- * @param key - The tenant option key
60
- */
61
- function getOrCreateFetcher(key) {
62
- let fetcher = tenantOptionFetchers[key];
63
- if (!fetcher) {
64
- fetcher = createCachedTenantOptionFetcher(key);
65
- tenantOptionFetchers[key] = fetcher;
66
- }
67
- return fetcher;
68
- }
69
- /**
70
- * Fetches a tenant option value by key.\
71
- * Uses the deployed tenant's service user credentials to access the Options API.\
72
- * Results are cached based on the configured TTL (default: 10 minutes).
73
- *
74
- * @param key - The tenant option key to fetch (as defined in `manifest.settings`)
75
- * @returns The option value as a string
76
- *
77
- * @config Cache TTL can be configured via:
78
- * - `c8y.cache.defaultTenantOptionsTTL` — Default TTL for all keys (in seconds)
79
- * - `c8y.cache.tenantOptions` — Per-key TTL overrides
80
- * - `NITRO_C8Y_DEFAULT_TENANT_OPTIONS_TTL` — Environment variable for default TTL
81
- *
82
- * @note For encrypted options (keys starting with `credentials.`), the value is automatically
83
- * decrypted by Cumulocity if this microservice is the owner of the option (category matches
84
- * the microservice's settingsCategory/contextPath/name). The `credentials.` prefix is
85
- * automatically stripped when calling the API.
86
- *
87
- * @example
88
- * // Fetch a tenant option:
89
- * const value = await useTenantOption('myOption')
90
- *
91
- * // Fetch an encrypted secret:
92
- * const secret = await useTenantOption('credentials.apiKey')
93
- *
94
- * // Invalidate cache for a specific key:
95
- * await useTenantOption.invalidate('myOption')
96
- *
97
- * // Force refresh a specific key:
98
- * const fresh = await useTenantOption.refresh('myOption')
99
- *
100
- * // Invalidate all tenant option caches:
101
- * await useTenantOption.invalidateAll()
102
- *
103
- * // Refresh all tenant options:
104
- * const all = await useTenantOption.refreshAll()
105
- */
106
- const useTenantOption = Object.assign(async (key) => {
107
- return await getOrCreateFetcher(key)();
108
- }, {
109
- invalidate: async (key) => {
110
- const fetcher = tenantOptionFetchers[key];
111
- if (fetcher) await fetcher.invalidate();
112
- },
113
- refresh: async (key) => {
114
- return await getOrCreateFetcher(key).refresh();
115
- },
116
- invalidateAll: async () => {
117
- await Promise.all(Object.values(tenantOptionFetchers).map((fetcher) => fetcher?.invalidate()));
118
- },
119
- refreshAll: async () => {
120
- const entries = Object.entries(tenantOptionFetchers);
121
- const values = await Promise.all(entries.map(([, fetcher]) => fetcher?.refresh()));
122
- return Object.fromEntries(entries.map(([key], i) => [key, values[i]]));
123
- }
124
- });
125
-
126
- //#endregion
127
- export { useTenantOption };