diff --git a/libs/common/oapi/services/auth.js b/libs/common/oapi/services/auth.js index 4765be6..5cf9b91 100644 --- a/libs/common/oapi/services/auth.js +++ b/libs/common/oapi/services/auth.js @@ -1,5 +1,4 @@ const { jwtVerify } = require("jose") -const jwtDecode = require("jwt-decode") const getHasuraClaimsFromJWT = require("@modjo/hasura/utils/jwt/get-hasura-claims-from-jwt") const { ctx } = require("@modjo/core") const { reqCtx } = require("@modjo/express/ctx") @@ -27,7 +26,7 @@ module.exports = function () { } return async function auth(jwt, scopes) { - const hasMetaExpUser = scopes.includes("meta.exp-user") + const hasMetaAuthToken = scopes.includes("meta.auth-token") let jwtVerified = false try { @@ -42,11 +41,11 @@ module.exports = function () { } catch (err) { const logger = ctx.require("logger") - // Allow expired JWT only if meta.exp-user scope is present - if (hasMetaExpUser && err.code === "ERR_JWT_EXPIRED") { + // Allow expired JWT only if meta.auth-token scope is present + if (hasMetaAuthToken && err.code === "ERR_JWT_EXPIRED") { logger.debug( { error: err }, - "Allowing expired JWT for meta.exp-user scope" + "Allowing expired JWT for meta.auth-token scope" ) // Continue processing with expired JWT } else { @@ -55,22 +54,23 @@ module.exports = function () { } } - const claims = getHasuraClaimsFromJWT(jwt, claimsNamespace) - const session = sessionVarsFromClaims(claims) + // For meta.auth-token scope, check for X-Auth-Token header + if (hasMetaAuthToken) { + const req = reqCtx.get("req") + const authTokenHeader = req?.headers?.["x-auth-token"] - // Add exp claim to session if meta.exp-user scope is present - if (hasMetaExpUser) { - try { - const payload = jwtDecode(jwt) - if (payload && payload.exp) { - session.exp = payload.exp - } - } catch (err) { - const logger = ctx.require("logger") - logger.error({ error: err }, "Failed to decode JWT for exp claim") + if (authTokenHeader) { + // Create a session that indicates auth token processing is needed + const session = { isAuthTokenRequest: true, authToken: authTokenHeader } + reqCtx.set("session", session) + return true } } + // Regular user JWT processing + const claims = getHasuraClaimsFromJWT(jwt, claimsNamespace) + const session = sessionVarsFromClaims(claims) + if (!isScopeAllowed(session, scopes)) { return false } diff --git a/libs/common/package.json b/libs/common/package.json index 0ada22a..df94184 100644 --- a/libs/common/package.json +++ b/libs/common/package.json @@ -5,9 +5,9 @@ "eslint": "^8.10.0" }, "dependencies": { - "@modjo/core": "*", - "@modjo/hasura": "*", - "@modjo/microservice-oapi": "*", + "@modjo/core": "^1.10.0", + "@modjo/hasura": "^1.10.0", + "@modjo/microservice-oapi": "^1.10.0", "@what3words/api": "^5.4.0", "fast-levenshtein": "^3.0.0", "fnv-plus": "^1.3.1", diff --git a/libs/redis-queue-dedup/package.json b/libs/redis-queue-dedup/package.json index 483daef..de1f854 100644 --- a/libs/redis-queue-dedup/package.json +++ b/libs/redis-queue-dedup/package.json @@ -2,7 +2,7 @@ "name": "redis-queue-dedup", "packageManager": "yarn@4.1.0", "dependencies": { - "@modjo/core": "^1.6.0", + "@modjo/core": "^1.10.0", "murmurhash": "^2.0.1" } } diff --git a/package.json b/package.json index d7c5db3..c4467ae 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "lint": "eslint .", "test": "jest tests", "clear:local": "git clean -xdf", - "postinstall": "[ -d '.husky' ] && husky install || true && bin/direnv allow" + "postinstall": "[ -d '.husky' ] && husky install || true && bin/direnv allow", + "up:modjo": "yarn up '@modjo/*'" }, "lint-staged": { "*.{js,jsx}": "eslint" diff --git a/services/api/package.json b/services/api/package.json index d51ae5a..c3fa3c2 100644 --- a/services/api/package.json +++ b/services/api/package.json @@ -14,15 +14,15 @@ }, "dependencies": { "@as/postgres-types": "workspace:^", - "@modjo/amqp": "*", - "@modjo/core": "*", - "@modjo/express": "*", - "@modjo/hasura": "*", - "@modjo/ioredis": "*", - "@modjo/microservice-oapi": "*", - "@modjo/oa-graphql": "*", - "@modjo/postgres": "*", - "@modjo/sentry": "*", + "@modjo/amqp": "^1.10.0", + "@modjo/core": "^1.10.0", + "@modjo/express": "^1.10.0", + "@modjo/hasura": "^1.10.0", + "@modjo/ioredis": "^1.10.0", + "@modjo/microservice-oapi": "^1.10.0", + "@modjo/oa-graphql": "^1.10.0", + "@modjo/postgres": "^1.10.0", + "@modjo/sentry": "^1.10.0", "@vercel/ncc": "^0.33.3", "argon2": "^0.31.0", "async": "^3.2.5", diff --git a/services/api/src/api/v1/operations/auth/login/token.patch.js b/services/api/src/api/v1/operations/auth/login/token.patch.js index 3190dad..e2e19df 100644 --- a/services/api/src/api/v1/operations/auth/login/token.patch.js +++ b/services/api/src/api/v1/operations/auth/login/token.patch.js @@ -1,151 +1,25 @@ -const httpError = require("http-errors") -const jwtDecode = require("jwt-decode") -const { nanoid } = require("nanoid") - -const { ctx } = require("@modjo/core") - -module.exports = async function ({ services: { sortRolesByLevel, signJwt } }) { - const config = ctx.require("config.project") - const sql = ctx.require("postgres") - - const { claimsNamespace, jwtExpirationInHours } = config - +module.exports = async function ({ services: { authTokenHandler } }) { async function doAuthLoginToken(req) { const { authTokenJwt, phoneModel = null, deviceUuid = null } = req.body - const { authToken } = jwtDecode(authTokenJwt) - let userId - let deviceId - let roles + // Validate the auth token JWT and extract the auth token + const authToken = authTokenHandler.validateAuthToken(authTokenJwt) - try { - const [row] = await sql` - SELECT - "user_id" as "userId", - "device_id" as "deviceId" - FROM - "auth_token" - WHERE - "auth_token" = ${authToken} - ` - userId = row.userId - deviceId = row.deviceId - } catch (e) { - throw httpError(410) - } - if (!userId) { - await sql.begin(async (sql) => { - await sql`set constraints all deferred` - ;[{ id: userId }] = await sql` - INSERT INTO "user" DEFAULT - VALUES - RETURNING - id - ` - ;[{ id: deviceId }] = await sql` - INSERT INTO "device" ("user_id", "phone_model", "uuid") - VALUES (${userId}, ${phoneModel}, ${deviceUuid}) - RETURNING - id - ` - await sql` - UPDATE - "auth_token" - SET - "user_id" = ${userId}, - "device_id" = ${deviceId} - WHERE - "auth_token" = ${authToken} - ` + // Get or create user session (userId, deviceId, roles) + const { userId, deviceId, roles } = + await authTokenHandler.getOrCreateUserSession( + authToken, + phoneModel, + deviceUuid + ) - const role = "user" - await sql` - INSERT INTO "user_role" ("user_id", "role") - VALUES (${userId}, ${role}) - ` - roles = [role] - - const authSignKey = nanoid() - await sql` - INSERT INTO "auth_sign_key" ("user_id", "key") - VALUES (${userId}, ${authSignKey}) - ` - }) - } else { - if (!deviceId && deviceUuid) { - // First check if a device with this UUID already exists for this user - const existingDevice = await sql` - SELECT - id - FROM - "device" - WHERE - "user_id" = ${userId} - AND "uuid" = ${deviceUuid} - LIMIT 1 - ` - - if (existingDevice.length > 0) { - deviceId = existingDevice[0].id - } - } - - if (!deviceId) { - // Only create new device if UUID doesn't exist - ;[{ id: deviceId }] = await sql` - INSERT INTO "device" ("user_id", "phone_model", "uuid") - VALUES (${userId}, ${phoneModel}, ${deviceUuid}) - RETURNING - id - ` - } - - // Update the auth_token to reference this device - await sql` - UPDATE - "auth_token" - SET - "device_id" = ${deviceId} - WHERE - "auth_token" = ${authToken} - ` - - roles = ( - await sql` - SELECT - "role" - FROM - "user_role" - WHERE - "user_id" = ${userId} - `.values() - ).map(([role]) => role) - } - - if (roles.length === 0) { - roles.push("user") - } - - const [defaultRole] = sortRolesByLevel(roles) - - const hasuraClaim = {} - hasuraClaim["x-hasura-default-role"] = defaultRole - hasuraClaim["x-hasura-allowed-roles"] = roles - hasuraClaim["x-hasura-user-id"] = userId.toString() - hasuraClaim["x-hasura-device-id"] = deviceId.toString() - - const exp = Math.round( - new Date(Date.now() + jwtExpirationInHours * 3600000) / 1000 + // Generate user JWT + const userBearerJwt = await authTokenHandler.generateUserJwt( + userId, + deviceId, + roles ) - // DEV - // const exp = Math.round(new Date(Date.now() + 5000) / 1000) - const jwtData = { - [claimsNamespace]: hasuraClaim, - exp, - } - - const userBearerJwt = await signJwt(jwtData) return { userBearerJwt } } diff --git a/services/api/src/api/v1/operations/geoloc/sync.post.js b/services/api/src/api/v1/operations/geoloc/sync.post.js index 071a38f..1422f2d 100644 --- a/services/api/src/api/v1/operations/geoloc/sync.post.js +++ b/services/api/src/api/v1/operations/geoloc/sync.post.js @@ -5,7 +5,7 @@ const { reqCtx } = require("@modjo/express/ctx") const tasks = require("~/tasks") -module.exports = function () { +module.exports = function ({ services: { authTokenHandler } }) { const { addTask } = ctx.require("amqp") const redis = ctx.require("redisHotGeodata") @@ -23,39 +23,69 @@ module.exports = function () { longitude, }, } = location - // console.log("addOneGeolocSync", req.body) const session = reqCtx.get("session") + let userId + let deviceId + let userBearerJwt = null - const { deviceId } = session + // Check if this is an auth token request (set by auth.js) + if (session && session.isAuthTokenRequest) { + // This is an auth token request, process it + try { + logger.debug("Processing auth token for geoloc sync") - // Check JWT expiration sequence to prevent replay attacks - if (session.exp) { - const deviceExpKey = `device:${deviceId}:last_exp` - const storedLastExp = await redis.get(deviceExpKey) + const { authToken } = session + const { + userId: newUserId, + deviceId: newDeviceId, + roles, + } = await authTokenHandler.getOrCreateUserSession( + authToken, + req.body.phoneModel, + req.body.deviceUuid + ) - if (storedLastExp && session.exp < parseInt(storedLastExp, 10)) { - throw httpError(401, "not the latest jwt") + userId = newUserId + deviceId = newDeviceId + + // Generate new user JWT for token refresh + userBearerJwt = await authTokenHandler.generateUserJwt( + userId, + deviceId, + roles + ) + + logger.debug({ + action: "geoloc-sync-auth-token", + userId, + deviceId, + tokenRefreshed: true, + }) + } catch (error) { + logger.error("Failed to process auth token", { error: error.message }) + throw httpError(401, "Invalid auth token") } - - // Store the new expiration date - await redis.set(deviceExpKey, session.exp) + } else if (session && session.userId && session.deviceId) { + // Regular user JWT session + userId = session.userId + deviceId = session.deviceId + logger.debug({ action: "geoloc-sync-user-jwt", userId, deviceId }) + } else { + // Invalid session + logger.error("Invalid session", { session }) + throw httpError(401, "Invalid session") } - const { userId } = session - - logger.debug({ action: "geoloc-sync", userId, deviceId }) + if (!userId || !deviceId) { + throw httpError(401, "Missing user or device information") + } const coordinates = [longitude, latitude] await async.parallel([ async () => { - // const transaction = redis.multi() - // transaction.geoadd("device", longitude, latitude, deviceId) - // transaction.publish("deviceSet", deviceId) - // await transaction.exec() await redis.geoadd("device", longitude, latitude, deviceId) - await addTask(tasks.GEOCODE_MOVE, { deviceId, userId, coordinates }) }, async () => @@ -70,7 +100,14 @@ module.exports = function () { }), ]) - return { ok: true } + const response = { ok: true } + + // Include userBearerJwt in response if token refresh occurred + if (userBearerJwt) { + response.userBearerJwt = userBearerJwt + } + + return response } return [addOneGeolocSync] diff --git a/services/api/src/api/v1/operations/geoloc/sync.post.spec.yaml b/services/api/src/api/v1/operations/geoloc/sync.post.spec.yaml index 97154a6..51bf4fa 100644 --- a/services/api/src/api/v1/operations/geoloc/sync.post.spec.yaml +++ b/services/api/src/api/v1/operations/geoloc/sync.post.spec.yaml @@ -1,6 +1,13 @@ # description: x-security: - - auth: ["user", "meta.exp-user"] + - auth: ["user", "meta.auth-token"] +parameters: + - name: X-Auth-Token + in: header + required: false + schema: + type: string + description: Auth token for token refresh when user JWT is expired requestBody: required: true content: @@ -101,3 +108,7 @@ responses: properties: ok: type: boolean + userBearerJwt: + type: string + description: New user JWT token when auth token refresh occurred + nullable: true diff --git a/services/api/src/api/v1/services/auth-token-handler.js b/services/api/src/api/v1/services/auth-token-handler.js new file mode 100644 index 0000000..f64c4b1 --- /dev/null +++ b/services/api/src/api/v1/services/auth-token-handler.js @@ -0,0 +1,169 @@ +const httpError = require("http-errors") +const jwtDecode = require("jwt-decode") +const { nanoid } = require("nanoid") + +const { ctx } = require("@modjo/core") + +module.exports = ({ services: { sortRolesByLevel, signJwt } }) => { + const config = ctx.require("config.project") + const sql = ctx.require("postgres") + + const { claimsNamespace, jwtExpirationInHours } = config + + async function validateAuthToken(authTokenJwt) { + try { + const { authToken } = jwtDecode(authTokenJwt) + return authToken + } catch (e) { + throw httpError(400, "Invalid auth token JWT") + } + } + + async function getOrCreateUserSession( + authToken, + phoneModel = null, + deviceUuid = null + ) { + let userId + let deviceId + let roles + + try { + const [row] = await sql` + SELECT + "user_id" as "userId", + "device_id" as "deviceId" + FROM + "auth_token" + WHERE + "auth_token" = ${authToken} + ` + userId = row.userId + deviceId = row.deviceId + } catch (e) { + throw httpError(410, "Auth token not found") + } + + if (!userId) { + await sql.begin(async (sql) => { + await sql`set constraints all deferred` + ;[{ id: userId }] = await sql` + INSERT INTO "user" DEFAULT + VALUES + RETURNING + id + ` + ;[{ id: deviceId }] = await sql` + INSERT INTO "device" ("user_id", "phone_model", "uuid") + VALUES (${userId}, ${phoneModel}, ${deviceUuid}) + RETURNING + id + ` + await sql` + UPDATE + "auth_token" + SET + "user_id" = ${userId}, + "device_id" = ${deviceId} + WHERE + "auth_token" = ${authToken} + ` + + const role = "user" + await sql` + INSERT INTO "user_role" ("user_id", "role") + VALUES (${userId}, ${role}) + ` + roles = [role] + + const authSignKey = nanoid() + await sql` + INSERT INTO "auth_sign_key" ("user_id", "key") + VALUES (${userId}, ${authSignKey}) + ` + }) + } else { + if (!deviceId && deviceUuid) { + // First check if a device with this UUID already exists for this user + const existingDevice = await sql` + SELECT + id + FROM + "device" + WHERE + "user_id" = ${userId} + AND "uuid" = ${deviceUuid} + LIMIT 1 + ` + + if (existingDevice.length > 0) { + deviceId = existingDevice[0].id + } + } + + if (!deviceId) { + // Only create new device if UUID doesn't exist + ;[{ id: deviceId }] = await sql` + INSERT INTO "device" ("user_id", "phone_model", "uuid") + VALUES (${userId}, ${phoneModel}, ${deviceUuid}) + RETURNING + id + ` + } + + // Update the auth_token to reference this device + await sql` + UPDATE + "auth_token" + SET + "device_id" = ${deviceId} + WHERE + "auth_token" = ${authToken} + ` + + roles = ( + await sql` + SELECT + "role" + FROM + "user_role" + WHERE + "user_id" = ${userId} + `.values() + ).map(([role]) => role) + } + + if (roles.length === 0) { + roles.push("user") + } + + return { userId, deviceId, roles } + } + + async function generateUserJwt(userId, deviceId, roles) { + const [defaultRole] = sortRolesByLevel(roles) + + const hasuraClaim = {} + hasuraClaim["x-hasura-default-role"] = defaultRole + hasuraClaim["x-hasura-allowed-roles"] = roles + hasuraClaim["x-hasura-user-id"] = userId.toString() + hasuraClaim["x-hasura-device-id"] = deviceId.toString() + + const exp = Math.round( + new Date(Date.now() + jwtExpirationInHours * 3600000) / 1000 + ) + + const jwtData = { + [claimsNamespace]: hasuraClaim, + exp, + } + + return signJwt(jwtData) + } + + return { + validateAuthToken, + getOrCreateUserSession, + generateUserJwt, + } +} diff --git a/services/files/package.json b/services/files/package.json index 766cce3..0d1744e 100644 --- a/services/files/package.json +++ b/services/files/package.json @@ -11,13 +11,13 @@ }, "dependencies": { "@as/postgres-types": "workspace:^", - "@modjo/amqp": "*", - "@modjo/core": "*", - "@modjo/express": "*", - "@modjo/microservice-oapi": "*", - "@modjo/minio": "*", - "@modjo/postgres": "*", - "@modjo/sentry": "^1.6.2", + "@modjo/amqp": "^1.10.0", + "@modjo/core": "^1.10.0", + "@modjo/express": "^1.10.0", + "@modjo/microservice-oapi": "^1.10.0", + "@modjo/minio": "^1.10.0", + "@modjo/postgres": "^1.10.0", + "@modjo/sentry": "^1.10.0", "@vercel/ncc": "^0.33.3", "common": "workspace:^", "lodash.tointeger": "^4.0.4", diff --git a/services/tasks/package.json b/services/tasks/package.json index 85bb199..59cd988 100644 --- a/services/tasks/package.json +++ b/services/tasks/package.json @@ -10,11 +10,11 @@ }, "dependencies": { "@as/postgres-types": "workspace:^", - "@modjo/core": "*", - "@modjo/ioredis": "^1.6.0", - "@modjo/microservice-worker": "*", - "@modjo/postgres": "*", - "@modjo/sentry": "^1.6.2", + "@modjo/core": "^1.10.0", + "@modjo/ioredis": "^1.10.0", + "@modjo/microservice-worker": "^1.10.0", + "@modjo/postgres": "^1.10.0", + "@modjo/sentry": "^1.10.0", "@vercel/ncc": "^0.30.0", "@what3words/api": "^4.0.4", "async": "^3.2.5", diff --git a/services/watchers/package.json b/services/watchers/package.json index 3d02ed8..c23ed4e 100644 --- a/services/watchers/package.json +++ b/services/watchers/package.json @@ -12,11 +12,11 @@ "dependencies": { "@apollo/client": "^3.5.8", "@as/postgres-types": "workspace:^", - "@modjo/core": "*", - "@modjo/ioredis": "*", - "@modjo/microservice-watcher": "*", - "@modjo/postgres": "*", - "@modjo/sentry": "^1.6.2", + "@modjo/core": "^1.10.0", + "@modjo/ioredis": "^1.10.0", + "@modjo/microservice-watcher": "^1.10.0", + "@modjo/postgres": "^1.10.0", + "@modjo/sentry": "^1.10.0", "@vercel/ncc": "^0.30.0", "async": "^3.2.5", "common": "workspace:^", diff --git a/yarn.lock b/yarn.lock index 82e2cc3..29f2425 100644 --- a/yarn.lock +++ b/yarn.lock @@ -314,15 +314,15 @@ __metadata: resolution: "@as/api@workspace:services/api" dependencies: "@as/postgres-types": "workspace:^" - "@modjo/amqp": "npm:*" - "@modjo/core": "npm:*" - "@modjo/express": "npm:*" - "@modjo/hasura": "npm:*" - "@modjo/ioredis": "npm:*" - "@modjo/microservice-oapi": "npm:*" - "@modjo/oa-graphql": "npm:*" - "@modjo/postgres": "npm:*" - "@modjo/sentry": "npm:*" + "@modjo/amqp": "npm:^1.10.0" + "@modjo/core": "npm:^1.10.0" + "@modjo/express": "npm:^1.10.0" + "@modjo/hasura": "npm:^1.10.0" + "@modjo/ioredis": "npm:^1.10.0" + "@modjo/microservice-oapi": "npm:^1.10.0" + "@modjo/oa-graphql": "npm:^1.10.0" + "@modjo/postgres": "npm:^1.10.0" + "@modjo/sentry": "npm:^1.10.0" "@vercel/ncc": "npm:^0.33.3" argon2: "npm:^0.31.0" async: "npm:^3.2.5" @@ -351,13 +351,13 @@ __metadata: resolution: "@as/files@workspace:services/files" dependencies: "@as/postgres-types": "workspace:^" - "@modjo/amqp": "npm:*" - "@modjo/core": "npm:*" - "@modjo/express": "npm:*" - "@modjo/microservice-oapi": "npm:*" - "@modjo/minio": "npm:*" - "@modjo/postgres": "npm:*" - "@modjo/sentry": "npm:^1.6.2" + "@modjo/amqp": "npm:^1.10.0" + "@modjo/core": "npm:^1.10.0" + "@modjo/express": "npm:^1.10.0" + "@modjo/microservice-oapi": "npm:^1.10.0" + "@modjo/minio": "npm:^1.10.0" + "@modjo/postgres": "npm:^1.10.0" + "@modjo/sentry": "npm:^1.10.0" "@vercel/ncc": "npm:^0.33.3" common: "workspace:^" link-module-alias: "npm:^1.2.0" @@ -380,11 +380,11 @@ __metadata: resolution: "@as/tasks@workspace:services/tasks" dependencies: "@as/postgres-types": "workspace:^" - "@modjo/core": "npm:*" - "@modjo/ioredis": "npm:^1.6.0" - "@modjo/microservice-worker": "npm:*" - "@modjo/postgres": "npm:*" - "@modjo/sentry": "npm:^1.6.2" + "@modjo/core": "npm:^1.10.0" + "@modjo/ioredis": "npm:^1.10.0" + "@modjo/microservice-worker": "npm:^1.10.0" + "@modjo/postgres": "npm:^1.10.0" + "@modjo/sentry": "npm:^1.10.0" "@vercel/ncc": "npm:^0.30.0" "@what3words/api": "npm:^4.0.4" async: "npm:^3.2.5" @@ -428,11 +428,11 @@ __metadata: dependencies: "@apollo/client": "npm:^3.5.8" "@as/postgres-types": "workspace:^" - "@modjo/core": "npm:*" - "@modjo/ioredis": "npm:*" - "@modjo/microservice-watcher": "npm:*" - "@modjo/postgres": "npm:*" - "@modjo/sentry": "npm:^1.6.2" + "@modjo/core": "npm:^1.10.0" + "@modjo/ioredis": "npm:^1.10.0" + "@modjo/microservice-watcher": "npm:^1.10.0" + "@modjo/postgres": "npm:^1.10.0" + "@modjo/sentry": "npm:^1.10.0" "@vercel/ncc": "npm:^0.30.0" async: "npm:^3.2.5" common: "workspace:^" @@ -3153,6 +3153,20 @@ __metadata: languageName: node linkType: hard +"@modjo/amqp@npm:^1.10.0": + version: 1.10.0 + resolution: "@modjo/amqp@npm:1.10.0" + dependencies: + "@modjo/config": "npm:*" + "@modjo/logger": "npm:*" + amqplib: "npm:^0.10.5" + nctx: "npm:^2.2.0" + wait-on: "npm:^6.0.1" + ya-retry: "npm:^1.2.0" + checksum: 10/37c15566a1d46ed900452330886e04f9861f24fb24f6aa49f2e8f74bf4994e9cde787a892b47849e458b390d3d20d88f54558c35124d21de338f6a9286b13400 + languageName: node + linkType: hard + "@modjo/apollo-client@npm:*": version: 1.9.6 resolution: "@modjo/apollo-client@npm:1.9.6" @@ -3179,7 +3193,7 @@ __metadata: languageName: node linkType: hard -"@modjo/core@npm:*, @modjo/core@npm:^1.6.0": +"@modjo/core@npm:*": version: 1.9.6 resolution: "@modjo/core@npm:1.9.6" dependencies: @@ -3205,6 +3219,32 @@ __metadata: languageName: node linkType: hard +"@modjo/core@npm:^1.10.0": + version: 1.10.0 + resolution: "@modjo/core@npm:1.10.0" + dependencies: + "@foundernetes/dbug": "npm:^1.0.0" + commander: "npm:^9.1.0" + fs-extra: "npm:^11.1.1" + js-yaml: "npm:^4.1.0" + link-module-alias: "npm:^1.2.0" + lodash: "npm:^4.17.21" + lodash.camelcase: "npm:^4.3.0" + lodash.capitalize: "npm:^4.2.1" + lodash.defaultsdeep: "npm:^4.6.1" + lodash.get: "npm:^4.4.2" + lodash.kebabcase: "npm:^4.1.1" + lodash.merge: "npm:^4.6.2" + lodash.mergewith: "npm:^4.6.2" + lodash.omit: "npm:^4.5.0" + lodash.set: "npm:^4.3.2" + nctx: "npm:^2.2.0" + pretty-ms: "npm:^7.0.1" + yup: "npm:^0.32.11" + checksum: 10/d29008c0bf8b5a932c46414469b1fc26fb979e263109b1e484b777ed4d7a41fff8a26b9dd071d92e9e94244a9b094f82d8ebc9680734458878418322d1d52197 + languageName: node + linkType: hard + "@modjo/express@npm:*": version: 1.9.6 resolution: "@modjo/express@npm:1.9.6" @@ -3225,6 +3265,26 @@ __metadata: languageName: node linkType: hard +"@modjo/express@npm:^1.10.0": + version: 1.10.0 + resolution: "@modjo/express@npm:1.10.0" + dependencies: + "@modjo/config": "npm:*" + "@modjo/http-logger": "npm:*" + "@modjo/http-server": "npm:*" + "@modjo/logger": "npm:*" + "@types/express": "npm:^5.0.0" + "@types/ws": "npm:^8.5.12" + cookie-parser: "npm:^1.4.6" + cors: "npm:^2.8.5" + express: "npm:^5.0.1" + nctx: "npm:^2.2.0" + websocket-express: "npm:^3.1.2" + ws: "npm:^8.18.0" + checksum: 10/026621b82f27d9f489748f8b9114f27b605c81ce299c5fe6e1895c0318ae29fbcf29817ed89d094ed58fcd274b2abda414cf3fbaad7e932d0b4e0ede0a35fde2 + languageName: node + linkType: hard + "@modjo/graphql-pubsub@npm:*": version: 1.9.6 resolution: "@modjo/graphql-pubsub@npm:1.9.6" @@ -3237,9 +3297,9 @@ __metadata: languageName: node linkType: hard -"@modjo/hasura@npm:*": - version: 1.9.6 - resolution: "@modjo/hasura@npm:1.9.6" +"@modjo/hasura@npm:^1.10.0": + version: 1.10.0 + resolution: "@modjo/hasura@npm:1.10.0" dependencies: "@modjo/config": "npm:*" axios: "npm:^1.4.0" @@ -3251,7 +3311,7 @@ __metadata: nctx: "npm:^2.2.0" postgres: "npm:^3.4.4" wait-on: "npm:^6.0.1" - checksum: 10/3adcf528d13888a7e3f81f54e1475c71b4eb39df82dffcdff354443a7091b1a59fc191714821af2cb4cf9582d07306227c0bc1204b258abcf55fb244c2574390 + checksum: 10/744bd911203f48c6e2046168998677550bf32dd959bd57455eff4cbdaed5c0ce02732413c9c9848718a496bd7f4f9819aa7296515edaf8a648cd06e76cb7da88 languageName: node linkType: hard @@ -3278,15 +3338,15 @@ __metadata: languageName: node linkType: hard -"@modjo/ioredis@npm:*, @modjo/ioredis@npm:^1.6.0": - version: 1.9.6 - resolution: "@modjo/ioredis@npm:1.9.6" +"@modjo/ioredis@npm:^1.10.0": + version: 1.10.0 + resolution: "@modjo/ioredis@npm:1.10.0" dependencies: "@modjo/config": "npm:*" ioredis: "npm:^5.3.2" nctx: "npm:^2.2.0" wait-on: "npm:^6.0.1" - checksum: 10/1b66d88c7f1a84693519965196c48764a42e62db6d4dd145e46ee757053fcedb0f93d919cca98a02662579199a0765c6bf0ab1f8597f72779ca3df26f2386fac + checksum: 10/324a3b2cbb5f43b80fc8445b907517c1cd077b72495051efdc733571641efaddac5d59d9829718882c3159172d65af0b8797fa5b15035ec8cdaadc131ed6beb7 languageName: node linkType: hard @@ -3332,21 +3392,39 @@ __metadata: languageName: node linkType: hard -"@modjo/microservice-watcher@npm:*": - version: 1.9.6 - resolution: "@modjo/microservice-watcher@npm:1.9.6" +"@modjo/microservice-oapi@npm:^1.10.0": + version: 1.10.0 + resolution: "@modjo/microservice-oapi@npm:1.10.0" + dependencies: + "@modjo/core": "npm:*" + "@modjo/express": "npm:*" + "@modjo/http-logger": "npm:*" + "@modjo/http-server": "npm:*" + "@modjo/lightship": "npm:*" + "@modjo/logger": "npm:*" + "@modjo/microservice-oapi": "npm:*" + "@modjo/oa": "npm:*" + "@modjo/shutdown-handlers": "npm:*" + nctx: "npm:^2.2.0" + checksum: 10/e6e4671d6718b98b7c4bacf4cfccc33bc6b812f418742ecfdd2b9a3870e4c2c81cd20d4fb813af7d62721dc0327747237b66e1e9824b1d3d6973df9d250a4929 + languageName: node + linkType: hard + +"@modjo/microservice-watcher@npm:^1.10.0": + version: 1.10.0 + resolution: "@modjo/microservice-watcher@npm:1.10.0" dependencies: "@modjo/amqp": "npm:*" "@modjo/apollo-client": "npm:*" "@modjo/core": "npm:*" nctx: "npm:^2.2.0" - checksum: 10/43a8c8d6cf323df9c57e6bbe400d700fd6a4d6d5c379d842454b05189e77a14461675864b3b72949e8d7b7fbebd3312f0f7c7aeba1ab1066216b236a5af9c970 + checksum: 10/543927812b86e2a52ac963441e4c14e3ab3a7b8e7d69de9f78086fd500adb6c5c2b86ac0a7bff182c477166be8fb7c1952935f9d5b3d1f5935899603a53c8a4f languageName: node linkType: hard -"@modjo/microservice-worker@npm:*": - version: 1.9.6 - resolution: "@modjo/microservice-worker@npm:1.9.6" +"@modjo/microservice-worker@npm:^1.10.0": + version: 1.10.0 + resolution: "@modjo/microservice-worker@npm:1.10.0" dependencies: "@modjo/amqp": "npm:*" "@modjo/config": "npm:*" @@ -3356,25 +3434,25 @@ __metadata: lodash.kebabcase: "npm:^4.1.1" nctx: "npm:^2.2.0" pretty-ms: "npm:^7.0.1" - checksum: 10/69ef8240d0db165dd11bb154811809ac82a31cf679bd4c7d76faa3b0129e4342cb4d57652892022c74cbdde2ae222145c1582fecddcf21fcc05d4ffdc2bf8559 + checksum: 10/74a2e2aa676742745109705dbde62b617bdae0bffa1bb4085b151c24dfcc9d5e1f75269835a1939478b7e64c19d96ca808d282a8ec385f058fab88f779f0dae2 languageName: node linkType: hard -"@modjo/minio@npm:*": - version: 1.9.6 - resolution: "@modjo/minio@npm:1.9.6" +"@modjo/minio@npm:^1.10.0": + version: 1.10.0 + resolution: "@modjo/minio@npm:1.10.0" dependencies: "@modjo/config": "npm:*" minio: "npm:^7.0.26" nctx: "npm:^2.2.0" wait-on: "npm:^6.0.1" - checksum: 10/71ab47c7b14681aaf92d44a370cfedceff4015afc5563931e9c7c2999141711ac8812c141adcd090a06236666327766ca7907a42a1174b8693003b4d86dbbba0 + checksum: 10/cb250571b01eb902ec93e42c17122527278a6fdfcc7f2f2fc0b75e1ea6aa8adbddb7dd8032379387a22ac336061b50b402255e50750dcdff0c8ab85cdcab92f3 languageName: node linkType: hard -"@modjo/oa-graphql@npm:*": - version: 1.9.6 - resolution: "@modjo/oa-graphql@npm:1.9.6" +"@modjo/oa-graphql@npm:^1.10.0": + version: 1.10.0 + resolution: "@modjo/oa-graphql@npm:1.10.0" dependencies: "@apollo/server": "npm:^4.7.5" "@apollo/server-plugin-landing-page-graphql-playground": "npm:^4.0.1" @@ -3398,7 +3476,7 @@ __metadata: nctx: "npm:^2.2.0" openapi-to-graphql: "npm:^3.0.5" subscriptions-transport-ws: "npm:^0.11.0" - checksum: 10/861db20404f67daf0a18fc18ac3086ad900f9fb0f8a378f6253bcb1f9002b8f2cc2b0e6c9c9d605d86451d64c9673510b544e3b0f84be0584c7c5cccf26c8a32 + checksum: 10/bba8eb52ac5a0bbe81061071190efd99d4348dd005f13c0b8ee7b52e9e17d58dd0a17fd1f9ae1d849bbc2c4d544f75da8423de35b2cb4cad0f67949808cde180 languageName: node linkType: hard @@ -3430,9 +3508,9 @@ __metadata: languageName: node linkType: hard -"@modjo/postgres@npm:*": - version: 1.9.6 - resolution: "@modjo/postgres@npm:1.9.6" +"@modjo/postgres@npm:^1.10.0": + version: 1.10.0 + resolution: "@modjo/postgres@npm:1.10.0" dependencies: "@opentelemetry/api": "npm:^1.9.0" "@opentelemetry/core": "npm:^1.25.1" @@ -3440,18 +3518,18 @@ __metadata: lodash.defaultsdeep: "npm:^4.6.1" nctx: "npm:^2.2.0" postgres: "npm:^3.4.4" - checksum: 10/bda561de7d9a0cd193eb1d7b0313b6a5eb5272b7fcab8c85315cfb4207feecec40a6cc0eaa89e94ab81facfc58266942a066bb90b3e2d9860c5b57ce1ea26339 + checksum: 10/b9711ff444f0f978d78c6a1d2cc4dac7e9816cbee2ee7395d2ac8e7b794606e22155e69a6debea99c592f58a63110f2207cd9dfdd199cc06d9bfa6814a9ed4cd languageName: node linkType: hard -"@modjo/sentry@npm:*, @modjo/sentry@npm:^1.6.2": - version: 1.9.6 - resolution: "@modjo/sentry@npm:1.9.6" +"@modjo/sentry@npm:^1.10.0": + version: 1.10.0 + resolution: "@modjo/sentry@npm:1.10.0" dependencies: "@sentry/node": "npm:^8.15.0" "@sentry/profiling-node": "npm:^8.15.0" nctx: "npm:^2.2.0" - checksum: 10/643113547d38022e75b12e4404f13fd02ddff83f19fa74c5a580ee9ff523297bcfb2abe3d75cf7973d2ced3d15c4da55e4f0fff1830bdd71d62ff941b9adf422 + checksum: 10/050e5d724fcc49542f839705c96765fdee1e185ba9f1d0c5bbb276fab5b755c4c6b2fe4a8c3e9519d44948149eca8506545e3713c76dcb14ac4aeefc87f65656 languageName: node linkType: hard @@ -7992,9 +8070,9 @@ __metadata: version: 0.0.0-use.local resolution: "common@workspace:libs/common" dependencies: - "@modjo/core": "npm:*" - "@modjo/hasura": "npm:*" - "@modjo/microservice-oapi": "npm:*" + "@modjo/core": "npm:^1.10.0" + "@modjo/hasura": "npm:^1.10.0" + "@modjo/microservice-oapi": "npm:^1.10.0" "@what3words/api": "npm:^5.4.0" eslint: "npm:^8.10.0" fast-levenshtein: "npm:^3.0.0" @@ -19490,7 +19568,7 @@ __metadata: version: 0.0.0-use.local resolution: "redis-queue-dedup@workspace:libs/redis-queue-dedup" dependencies: - "@modjo/core": "npm:^1.6.0" + "@modjo/core": "npm:^1.10.0" murmurhash: "npm:^2.0.1" languageName: unknown linkType: soft