From 5c45fc15941ef741e70296c963dfcc36e8932e0c Mon Sep 17 00:00:00 2001 From: mohamed mahmoud <115894267+mohamed-ctrl878@users.noreply.github.com> Date: Wed, 16 Sep 2026 22:10:05 +0300 Subject: [PATCH 1/9] chore(deps): add @socket.io/redis-adapter and redis client Needed so Socket.io room-based broadcasts (notifications, submissions) work correctly across multiple Strapi instances instead of only the instance that received the event. --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 4a3c58b..f98dcbb 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@sensinum/strapi-plugin-mcp": "^1.1.0", + "@socket.io/redis-adapter": "^8.3.0", "@strapi/plugin-cloud": "^5.16.0", "@strapi/plugin-users-permissions": "^5.16.0", "@strapi/provider-email-nodemailer": "^5.16.0", @@ -29,6 +30,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.30.3", + "redis": "^4.7.0", "sendmail": "^1.6.1", "socket.io": "^4.8.1", "socket.io-client": "^4.8.3", From 00c00d45d69a8f9a901f76594d87d06977e2b9db Mon Sep 17 00:00:00 2001 From: mohamed mahmoud <115894267+mohamed-ctrl878@users.noreply.github.com> Date: Wed, 16 Sep 2026 22:10:54 +0300 Subject: [PATCH 2/9] feat(socket): attach Redis adapter to Socket.io for horizontal scaling Both submission-socket and notification-socket broadcast via `io.to(room).emit(...)`. Socket.io's default in-memory adapter keeps room membership local to each process, so with more than one Strapi instance behind a load balancer, a client connected to instance B never receives events emitted from instance A even if it joined the same room. This silently drops real-time notifications/submission updates for a subset of users instead of throwing an error. - Adds Redis pub/sub adapter, gated on REDIS_URL - Logs a loud warning (not a silent fallback) when REDIS_URL is missing, since that failure mode is otherwise very hard to notice until you scale past one instance - No behavior change for the current single-instance deployment See AxeCode_QA_Scalability_Plan.md section 4.2 for the full analysis. --- src/index.js | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index d56d63b..73823d3 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,8 @@ 'use strict'; const { Server } = require('socket.io'); +const { createAdapter } = require('@socket.io/redis-adapter'); +const { createClient } = require('redis'); const { createFileOwnershipMiddleware } = require('./middlewares/file-ownership-middleware'); const { createLessonAccessStrategy } = require('./api/upload-security/strategies/lesson-access-strategy'); const { createAuthenticatedAccessStrategy } = require('./api/upload-security/strategies/authenticated-access-strategy'); @@ -108,7 +110,7 @@ module.exports = { } })(); - // Initialize Socket.io + // ── Initialize Socket.io ── const io = new Server(strapi.server.httpServer, { cors: { origin: [ @@ -122,6 +124,41 @@ module.exports = { strapi.io = io; + // ── Layer 3 (Scalability): Attach Redis Adapter so room-based broadcasts ── + // (`io.to('user:x').emit(...)`, used by notification-socket and + // submission-socket) reach clients connected to ANY Strapi instance, not + // only the instance that triggered the emit. Without this, running more + // than one Strapi replica behind a load balancer silently drops real-time + // events for users whose socket landed on a different instance. + // + // REDIS_URL is required for this. If it's missing we deliberately do NOT + // silently fall back to the in-memory adapter in production, because that + // failure mode (events randomly not arriving) is far harder to diagnose + // than a loud startup warning. + if (process.env.REDIS_URL) { + const pubClient = createClient({ url: process.env.REDIS_URL }); + const subClient = pubClient.duplicate(); + + pubClient.on('error', (err) => strapi.log.error(`[Socket.io/Redis] pubClient error: ${err.message}`)); + subClient.on('error', (err) => strapi.log.error(`[Socket.io/Redis] subClient error: ${err.message}`)); + + Promise.all([pubClient.connect(), subClient.connect()]) + .then(() => { + io.adapter(createAdapter(pubClient, subClient)); + strapi.log.info('[Socket.io] Redis adapter attached — safe for multi-instance horizontal scaling'); + }) + .catch((err) => { + strapi.log.error(`[Socket.io] Failed to attach Redis adapter, falling back to in-memory adapter (NOT safe for >1 instance): ${err.message}`); + }); + } else { + strapi.log.warn( + '[Socket.io] REDIS_URL not set — using in-memory adapter. ' + + 'This is fine for a single instance, but real-time notifications/submission ' + + 'updates WILL be silently dropped for some users if you run more than one ' + + 'Strapi instance/replica behind a load balancer. Set REDIS_URL before scaling horizontally.' + ); + } + // Initialize submission socket handlers strapi.service('api::submission.submission-socket').initialize(io); From 530054fbd7d220f04b9624c52c442820ebb0b2a0 Mon Sep 17 00:00:00 2001 From: mohamed mahmoud <115894267+mohamed-ctrl878@users.noreply.github.com> Date: Wed, 16 Sep 2026 22:11:09 +0300 Subject: [PATCH 3/9] docs(env): document REDIS_URL for Socket.io Redis adapter --- .env.example | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.env.example b/.env.example index 0672c59..37055e2 100644 --- a/.env.example +++ b/.env.example @@ -22,6 +22,12 @@ DATABASE_PASSWORD=your_secure_password DATABASE_SSL=false DATABASE_FILENAME= # Used only if DATABASE_CLIENT=sqlite +# ── Redis (Socket.io Adapter — required once you run more than one instance) ── +# Without this, real-time notifications/submission updates will be silently +# dropped for users connected to a different instance than the one that +# emitted the event. Safe to leave empty for local single-instance dev. +REDIS_URL=redis://localhost:6379 + # ── Email Strategy (GMAIL) ── GMAIL_USER=your-email@gmail.com GMAIL_APP_PASSWORD=your-google-app-password From c0057394e1dc36547217760d6f164d2cc55105ed Mon Sep 17 00:00:00 2001 From: mohamed mahmoud <115894267+mohamed-ctrl878@users.noreply.github.com> Date: Wed, 16 Sep 2026 22:11:23 +0300 Subject: [PATCH 4/9] docs(env): document REDIS_URL for Socket.io Redis adapter (production) --- .env.production.example | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.env.production.example b/.env.production.example index e3af617..0a3b4c6 100644 --- a/.env.production.example +++ b/.env.production.example @@ -25,6 +25,12 @@ DATABASE_USERNAME=axecode DATABASE_PASSWORD=CHANGE_ME_STRONG_PASSWORD DATABASE_SSL=false +# Redis (Socket.io Adapter — REQUIRED before running more than one instance). +# Matches the `app-redis` service in docker-compose.yml. Without this set, +# real-time notifications/submission updates are silently dropped for users +# whose socket lands on a different instance than the one that emitted the event. +REDIS_URL=redis://app-redis:6379 + # CORS (your Vercel frontend URL) CORS_ORIGIN=https://axe-code.vercel.app FRONTEND_URL=https://axe-code.vercel.app From 7c7432f01194c6644cfba39002a2078781dd834d Mon Sep 17 00:00:00 2001 From: mohamed mahmoud <115894267+mohamed-ctrl878@users.noreply.github.com> Date: Wed, 16 Sep 2026 22:11:45 +0300 Subject: [PATCH 5/9] infra: add dedicated app-redis service for Socket.io adapter Separate from judge0-redis (different lifecycle/blast-radius): this one backs real-time notifications and submission-update broadcasts and is required before scaling the strapi service to >1 replica. --- docker-compose.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 8422110..35bf4eb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,7 @@ services: - DATABASE_USERNAME=axecode - DATABASE_SSL=false - JUDGE0_API_URL=http://judge0-api:2358 + - REDIS_URL=redis://app-redis:6379 ports: - "1338:1338" volumes: @@ -25,6 +26,8 @@ services: depends_on: db: condition: service_healthy + app-redis: + condition: service_healthy networks: - axe-network healthcheck: @@ -68,6 +71,33 @@ services: max-size: "50m" max-file: "3" + # ============================================ + # App Redis — Socket.io Adapter (pub/sub for real-time + # notifications + submission updates across Strapi instances). + # Kept separate from judge0-redis on purpose: different lifecycle, + # different failure blast-radius (this one is app-critical for + # real-time features; judge0-redis is internal to the execution engine). + # ============================================ + app-redis: + image: redis:7-alpine + container_name: axe-app-redis + restart: unless-stopped + command: ["redis-server", "--appendonly", "yes"] + volumes: + - app-redis-data:/data + networks: + - axe-network + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + logging: + driver: "json-file" + options: + max-size: "50m" + max-file: "3" + # ============================================ # Nginx Proxy Manager (SSL & Proxy) # ============================================ @@ -172,6 +202,8 @@ volumes: driver: local judge0-db-data: driver: local + app-redis-data: + driver: local # ============================================ # Network From 0c8a3088c625414c12c03ce6eaee4a77e3ab0bb0 Mon Sep 17 00:00:00 2001 From: mohamed mahmoud <115894267+mohamed-ctrl878@users.noreply.github.com> Date: Wed, 16 Sep 2026 22:12:01 +0300 Subject: [PATCH 6/9] infra(render): add REDIS_URL env var placeholder for Socket.io adapter --- render.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/render.yaml b/render.yaml index cd4d2d7..455fa8b 100644 --- a/render.yaml +++ b/render.yaml @@ -24,6 +24,14 @@ services: - key: DATABASE_SSL value: "false" # Render internal DB connections don't use SSL + # Redis (Socket.io Adapter). Render's free tier has no managed Redis, + # so point this at an external free-tier instance (e.g. Upstash, Redis + # Cloud) — fill in the Render Dashboard. Required once this service + # runs as more than one instance, otherwise fine to leave unset on a + # single staging instance. + - key: REDIS_URL + sync: false + # Auto-generated secrets - key: APP_KEYS generateValue: true From 565d033162be50e0309305e6087703c382a06b41 Mon Sep 17 00:00:00 2001 From: mohamed mahmoud Date: Wed, 16 Sep 2026 22:38:51 +0300 Subject: [PATCH 7/9] fix: address security report vulnerabilities (IDOR, rate limit, session) --- config/middlewares.js | 2 +- src/api/auth/services/security-pipeline.js | 25 +++++++++++------ src/api/submission/controllers/submission.js | 29 +++++++++++++++++++- src/middlewares/github-oauth-debug.js | 4 +++ 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/config/middlewares.js b/config/middlewares.js index bdeee8c..d250929 100644 --- a/config/middlewares.js +++ b/config/middlewares.js @@ -63,7 +63,7 @@ module.exports = ({ env }) => { { name: "strapi::session", config: { - secure: false, + secure: env('NODE_ENV') === 'production', }, }, "strapi::favicon", diff --git a/src/api/auth/services/security-pipeline.js b/src/api/auth/services/security-pipeline.js index 9854537..ed9a08e 100644 --- a/src/api/auth/services/security-pipeline.js +++ b/src/api/auth/services/security-pipeline.js @@ -7,6 +7,15 @@ const rateLimits = new Map(); // Simple in-memory rate limiter +// Memory leak prevention: Clean up expired IPs every 5 minutes +setInterval(() => { + const now = Date.now(); + for (const [ip, record] of rateLimits.entries()) { + if (now > record.resetTime) { + rateLimits.delete(ip); + } + } +}, 5 * 60 * 1000); const { z } = require('zod'); const validator = require('validator'); @@ -136,25 +145,25 @@ module.exports = ({ strapi }) => ({ try { const securityLogic = strapi.service('api::auth.security-logic'); let token = ctx.cookies.get('jwt'); - console.log("[SECURITY DEBUG] Cookie JWT present:", !!token); + strapi.log.debug(`[SECURITY DEBUG] Cookie JWT present: ${!!token}`); if (!token && ctx.request?.header?.authorization) { const parts = ctx.request.header.authorization.split(' '); if (parts.length === 2 && parts[0].toLowerCase() === 'bearer') { token = parts[1]; - console.log("[SECURITY DEBUG] Token extracted from Header"); + strapi.log.debug("[SECURITY DEBUG] Token extracted from Header"); } } if (!token) { - console.log("[SECURITY DEBUG] No token found in Cookie or Header"); + strapi.log.debug("[SECURITY DEBUG] No token found in Cookie or Header"); return; } const payload = await securityLogic.verifyToken(token); - console.log("[SECURITY DEBUG] Payload verified:", !!payload); + strapi.log.debug(`[SECURITY DEBUG] Payload verified: ${!!payload}`); if (!payload || !payload.id) { strapi.log.debug('[Security] Malformed or incompatible JWT payload detected'); @@ -165,17 +174,17 @@ module.exports = ({ strapi }) => ({ where: { id: payload.id }, populate: { role: true } }); - console.log("[SECURITY DEBUG] User found in DB:", !!user); + strapi.log.debug(`[SECURITY DEBUG] User found in DB: ${!!user}`); if (user && !user.blocked) { ctx.state.user = user; - console.log("[SECURITY DEBUG] Auth successful for user ID:", user.id); + strapi.log.debug(`[SECURITY DEBUG] Auth successful for user ID: ${user.id}`); } else if (user?.blocked) { - console.log("[SECURITY DEBUG] User is blocked"); + strapi.log.debug("[SECURITY DEBUG] User is blocked"); } } catch (err) { strapi.log.error(`[Security] Authentication error: ${err.message}`); - console.error("[SECURITY DEBUG] Exception:", err); + strapi.log.debug(`[SECURITY DEBUG] Exception: ${err.message}`); } } }); diff --git a/src/api/submission/controllers/submission.js b/src/api/submission/controllers/submission.js index c6256c3..d9c366f 100644 --- a/src/api/submission/controllers/submission.js +++ b/src/api/submission/controllers/submission.js @@ -57,6 +57,27 @@ module.exports = createCoreController("api::submission.submission", ({ strapi }) return this.transformResponse(sanitizedResults, { pagination }); }, + async findOne(ctx) { + const { id } = ctx.params; + const loggedInUser = ctx.state.user; + + const entity = await strapi.service('api::submission.submission').findOne(id, { + populate: ctx.query.populate, + }); + + if (!entity) return ctx.notFound(); + + const ownerId = entity.user?.id || entity.user; + const isOwner = loggedInUser && (ownerId === loggedInUser.id); + + const result = isOwner + ? entity + : { ...entity, code: '/* Private Code */', judgeOutput: { results: [] } }; + + const sanitized = await this.sanitizeOutput(result, ctx); + return this.transformResponse(sanitized); + }, + /** * Handle code submission */ @@ -99,10 +120,16 @@ module.exports = createCoreController("api::submission.submission", ({ strapi }) */ async testSubmit(ctx) { const { problem, code, language } = ctx.request.body.data; + const user = ctx.state.user; + + if (!user) { + return ctx.unauthorized('You must be logged in to perform test submissions'); + } + if (!problem || !code || !language) return ctx.badRequest('Missing fields'); const result = await strapi.documents('api::submission.submission').create({ - data: { problem, code, language, verdict: 'pending' } + data: { problem, code, language, user: user.id, verdict: 'pending' } }); strapi.service('api::submission.submission-logic').queueSubmission(result.documentId); diff --git a/src/middlewares/github-oauth-debug.js b/src/middlewares/github-oauth-debug.js index dff28bf..47f8507 100644 --- a/src/middlewares/github-oauth-debug.js +++ b/src/middlewares/github-oauth-debug.js @@ -9,6 +9,10 @@ module.exports = (config, { strapi }) => { return async (ctx, next) => { const url = ctx.request.url; + if (process.env.NODE_ENV === 'production') { + return next(); + } + // Only log for OAuth connect routes if (url.includes('/connect/github')) { strapi.log.info(`\n========== [GITHUB OAUTH DEBUG] ==========`); From b08ebd74c405fef775d0f6ccdf66933d479912d1 Mon Sep 17 00:00:00 2001 From: mohamed mahmoud Date: Wed, 16 Sep 2026 22:40:41 +0300 Subject: [PATCH 8/9] chore: sync package-lock.json with new redis dependencies --- package-lock.json | 125 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/package-lock.json b/package-lock.json index 4b37f6e..c10d3ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@sensinum/strapi-plugin-mcp": "^1.1.0", + "@socket.io/redis-adapter": "^8.3.0", "@strapi/plugin-cloud": "^5.16.0", "@strapi/plugin-users-permissions": "^5.16.0", "@strapi/provider-email-nodemailer": "^5.16.0", @@ -23,6 +24,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.30.3", + "redis": "^4.7.0", "sendmail": "^1.6.1", "socket.io": "^4.8.1", "socket.io-client": "^4.8.3", @@ -7958,6 +7960,71 @@ "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==", "license": "MIT" }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", + "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/client/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@reduxjs/toolkit": { "version": "1.9.7", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", @@ -9609,6 +9676,23 @@ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "license": "MIT" }, + "node_modules/@socket.io/redis-adapter": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@socket.io/redis-adapter/-/redis-adapter-8.3.0.tgz", + "integrity": "sha512-ly0cra+48hDmChxmIpnESKrc94LjRL80TEmZVscuQ/WWkRP81nNj8W8cCGMqbI4L6NCuAaPRSzZF1a9GlAxxnA==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.1", + "notepack.io": "~3.0.1", + "uid2": "1.0.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "socket.io-adapter": "^2.5.4" + } + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -22726,6 +22810,15 @@ "node": ">= 0.4" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -29461,6 +29554,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/notepack.io": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-3.0.1.tgz", + "integrity": "sha512-TKC/8zH5pXIAMVQio2TvVDTtPRX+DJPHDqjRbxogtFiByHyzKmy96RA0JtCQJ+WouyyL4A10xomQzgbUT+1jCg==", + "license": "MIT" + }, "node_modules/npm-bundled": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", @@ -32539,6 +32638,23 @@ "node": ">= 10.13.0" } }, + "node_modules/redis": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz", + "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==", + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.1", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -37066,6 +37182,15 @@ "node": ">=0.8.0" } }, + "node_modules/uid2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-1.0.0.tgz", + "integrity": "sha512-+I6aJUv63YAcY9n4mQreLUt0d4lvwkkopDNmpomkAUz0fAkEMV9pRWxN0EjhW1YfRhcuyHg2v3mwddCDW1+LFQ==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/uint8array-extras": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", From 629a277051bf93c1665552009b7b65535318aee9 Mon Sep 17 00:00:00 2001 From: mohamed mahmoud Date: Sat, 26 Sep 2026 17:24:13 +0300 Subject: [PATCH 9/9] fix: set session secure false to fix github oauth behind proxy --- config/middlewares.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/middlewares.js b/config/middlewares.js index d250929..97b39d6 100644 --- a/config/middlewares.js +++ b/config/middlewares.js @@ -63,7 +63,7 @@ module.exports = ({ env }) => { { name: "strapi::session", config: { - secure: env('NODE_ENV') === 'production', + secure: false, // Fixes "Cannot send secure cookie over unencrypted connection" when behind proxy }, }, "strapi::favicon",