From 6a42d12fda6119da5c3d4c5bb028c2bafa39e061 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 19 Jan 2026 13:05:09 +0100 Subject: [PATCH 1/5] fix(webapp): fix cta-join-group, can crash when group is not defined (#9103) --- webapp/pages/post/_id/_slug/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/pages/post/_id/_slug/index.vue b/webapp/pages/post/_id/_slug/index.vue index 6c901977f..88616b115 100644 --- a/webapp/pages/post/_id/_slug/index.vue +++ b/webapp/pages/post/_id/_slug/index.vue @@ -154,7 +154,7 @@ > From 0ca45dd06e9b899e4bcb09850eb0401d1cb0f1c6 Mon Sep 17 00:00:00 2001 From: mahula Date: Mon, 19 Jan 2026 16:00:40 +0100 Subject: [PATCH 2/5] refactor(e2e): optimize step definitions loading with filepart pairing (#9122) --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8975dd936..0c64ecbd9 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,10 @@ "url": "https://github.com/Ocelot-Social-Community/Ocelot-Social.git" }, "cypress-cucumber-preprocessor": { - "stepDefinitions": "cypress/support/step_definitions/**/*.js", + "stepDefinitions": [ + "cypress/support/step_definitions/[filepart]/**/*.js", + "cypress/support/step_definitions/common/**/*.js" + ], "json": { "enabled": true, "output": "cypress/reports/json_logs/cucumber_log.json", From b22974031cf249fab44168cef1e2309858bdccc8 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 19 Jan 2026 18:48:25 +0100 Subject: [PATCH 3/5] fix(backend): fix group-myRole field query (#9102) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- backend/src/graphql/resolvers/groups.ts | 71 +++++++++++-------------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/backend/src/graphql/resolvers/groups.ts b/backend/src/graphql/resolvers/groups.ts index 1213c15a9..9ccf342d3 100644 --- a/backend/src/graphql/resolvers/groups.ts +++ b/backend/src/graphql/resolvers/groups.ts @@ -24,9 +24,6 @@ export default { Query: { Group: async (_object, params, context: Context, _resolveInfo) => { const { isMember, id, slug, first, offset } = params - let pagination = '' - const orderBy = 'ORDER BY group.createdAt DESC' - if (first !== undefined && offset !== undefined) pagination = `SKIP ${offset} LIMIT ${first}` const matchParams = { id, slug } removeUndefinedNullValuesFromObject(matchParams) const session = context.driver.session() @@ -34,43 +31,22 @@ export default { if (!context.user) { throw new Error('Missing authenticated user.') } - const groupMatchParamsCypher = convertObjectToCypherMapLiteral(matchParams, true) - let groupCypher - if (isMember === true) { - groupCypher = ` - MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group:Group${groupMatchParamsCypher}) - WITH group, membership - WHERE (group.groupType IN ['public', 'closed']) OR (group.groupType = 'hidden' AND membership.role IN ['usual', 'admin', 'owner']) - RETURN group {.*, myRole: membership.role} - ${orderBy} - ${pagination} + const transactionResponse = await txc.run( ` - } else { - if (isMember === false) { - groupCypher = ` - MATCH (group:Group${groupMatchParamsCypher}) - WHERE (NOT (:User {id: $userId})-[:MEMBER_OF]->(group)) - WITH group - WHERE group.groupType IN ['public', 'closed'] - RETURN group {.*, myRole: NULL} - ${orderBy} - ${pagination} - ` - } else { - groupCypher = ` - MATCH (group:Group${groupMatchParamsCypher}) - OPTIONAL MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group) - WITH group, membership - WHERE (group.groupType IN ['public', 'closed']) OR (group.groupType = 'hidden' AND membership.role IN ['usual', 'admin', 'owner']) - RETURN group {.*, myRole: membership.role} - ${orderBy} - ${pagination} - ` - } - } - const transactionResponse = await txc.run(groupCypher, { - userId: context.user.id, - }) + MATCH (group:Group${convertObjectToCypherMapLiteral(matchParams, true)}) + OPTIONAL MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group) + WITH group, membership + ${(isMember === true && "WHERE membership IS NOT NULL AND (group.groupType IN ['public', 'closed']) OR (group.groupType = 'hidden' AND membership.role IN ['usual', 'admin', 'owner'])") || ''} + ${(isMember === false && "WHERE membership IS NULL AND (group.groupType IN ['public', 'closed'])") || ''} + ${(isMember === undefined && "WHERE (group.groupType IN ['public', 'closed']) OR (group.groupType = 'hidden' AND membership.role IN ['usual', 'admin', 'owner'])") || ''} + RETURN group {.*, myRole: membership.role} + ORDER BY group.createdAt DESC + ${first !== undefined && offset !== undefined ? `SKIP ${offset} LIMIT ${first}` : ''} + `, + { + userId: context.user.id, + }, + ) return transactionResponse.records.map((record) => record.get('group')) }) try { @@ -460,6 +436,23 @@ export default { }, }, Group: { + myRole: async (parent, _args, context: Context, _resolveInfo) => { + if (!parent.id) { + throw new Error('Can not identify selected Group!') + } + return ( + await context.database.query({ + query: ` + MATCH (:User {id: $user.id})-[membership:MEMBER_OF]->(group:Group {id: $parent.id}) + RETURN membership.role as role + `, + variables: { + user: context.user, + parent, + }, + }) + ).records.map((r) => r.get('role'))[0] + }, inviteCodes: async (parent, _args, context: Context, _resolveInfo) => { if (!parent.id) { throw new Error('Can not identify selected Group!') From f0f9b7faecdf0f68f5c9d75312c9e9613b0bd730 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 19 Jan 2026 19:14:17 +0100 Subject: [PATCH 4/5] fix(backend): fix permissions for GroupInviteCodes (#9121) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- backend/src/middleware/permissionsMiddleware.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index 5fc087204..0e85a43af 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -387,7 +387,7 @@ const isAllowedToGenerateGroupInviteCode = rule({ return !!( await context.database.query({ query: ` - MATCH (user:User{id: user.id})-[membership:MEMBER_OF]->(group:Group {id: $args.groupId}) + MATCH (user:User{id: $user.id})-[membership:MEMBER_OF]->(group:Group {id: $args.groupId}) WHERE (group.type IN ['closed','hidden'] AND membership.role IN ['admin', 'owner']) OR (NOT group.type IN ['closed','hidden'] AND NOT membership.role = 'pending') RETURN count(group) as count From 150b318aabea30f09930decb4f2468424bfb6299 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 19 Jan 2026 19:36:20 +0100 Subject: [PATCH 5/5] feat(backend): admin creation command for production (#9057) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- backend/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index a94ca620d..058e636a1 100644 --- a/backend/package.json +++ b/backend/package.json @@ -25,7 +25,8 @@ "db:migrate:create": "migrate --compiler 'ts:./src/db/compiler.ts' --migrations-dir ./src/db/migrations --template-file ./src/db/migrate/template.ts --date-format 'yyyymmddHHmmss' create", "prod:migrate": "migrate --migrations-dir ./build/src/db/migrations --store ./build/src/db/migrate/store.js", "prod:db:data:branding": "node build/src/db/data-branding.js", - "prod:db:data:categories": "node build/src/db/categories.js" + "prod:db:data:categories": "node build/src/db/categories.js", + "prod:db:data:admin": "node build/src/db/admin.js" }, "dependencies": { "@aws-sdk/client-s3": "^3.971.0",