Add code resolver, sync conflict handling, and user isolation

Introduce a CodeResolverService to classify user login codes, complete with detailed resolution logic and usability checks. Extend the sync system to handle conflicts via last-write-wins arbitration, with detailed conflict tracking for review. Update file permissions and runtime isolation in Docker to enhance security.
This commit is contained in:
2026-09-12 15:40:42 +02:00
parent 858c43a6aa
commit 5fd2d47a64
19 changed files with 950 additions and 131 deletions
+20 -7
View File
@@ -25,12 +25,20 @@ export class OnboardingService {
/// Public: resolves an invite code to the KC name and its Gemeinden so the
/// registrant can pick theirs. The code itself is the shared secret.
async resolveInvite(inviteCode: string) {
const kc = await this.prisma.kc.findUnique({
where: { inviteCode },
include: {
gemeinden: { select: { id: true, name: true }, orderBy: { name: 'asc' } },
},
});
const trimmed = inviteCode.trim();
const kc =
(await this.prisma.kc.findFirst({
where: { inviteCode: { equals: trimmed, mode: 'insensitive' } },
include: {
gemeinden: { select: { id: true, name: true }, orderBy: { name: 'asc' } },
},
})) ||
(await this.prisma.kc.findUnique({
where: { inviteCode: trimmed },
include: {
gemeinden: { select: { id: true, name: true }, orderBy: { name: 'asc' } },
},
}));
if (!kc || !kc.isActive) {
throw new NotFoundException('Unknown or inactive KC invite code');
}
@@ -43,7 +51,12 @@ export class OnboardingService {
}
const { isLeitungsteam, ...claims } = await this.tokens.verifyAuthentikClaims(token);
const kc = await this.prisma.kc.findUnique({ where: { inviteCode } });
const trimmed = inviteCode.trim();
const kc =
(await this.prisma.kc.findFirst({
where: { inviteCode: { equals: trimmed, mode: 'insensitive' } },
})) ||
(await this.prisma.kc.findUnique({ where: { inviteCode: trimmed } }));
if (!kc || !kc.isActive) {
throw new NotFoundException('Unknown or inactive KC invite code');
}