fix: Major bug cleanup pass 1
All checks were successful
Queue Release Build / prepare (push) Successful in 19s
Deploy Web Apps / deploy (push) Successful in 8m12s
Queue Release Build / build-windows (push) Successful in 27m44s
Queue Release Build / build-linux (push) Successful in 48m1s
Queue Release Build / build-android (push) Successful in 22m7s
Queue Release Build / finalize (push) Successful in 2m42s

This commit is contained in:
2026-06-09 17:59:54 +02:00
parent 80d7728e66
commit eb51f043ac
127 changed files with 2731 additions and 322 deletions

View File

@@ -41,7 +41,7 @@ function buildCorsOptions() {
export function createApp(): express.Express {
const app = express();
// Trust loopback proxies only avoids express-rate-limit ERR_ERL_PERMISSIVE_TRUST_PROXY.
// Trust loopback proxies only - avoids express-rate-limit ERR_ERL_PERMISSIVE_TRUST_PROXY.
app.set('trust proxy', 'loopback');
app.use(cors(buildCorsOptions()));
app.use(express.json());

View File

@@ -0,0 +1,30 @@
import {
describe,
it,
expect
} from 'vitest';
import { isDuplicateUsernameError } from './user-registration.rules';
describe('user-registration.rules', () => {
it('detects sqlite unique constraint failures on username', () => {
expect(isDuplicateUsernameError({
message: 'UNIQUE constraint failed: users.username'
})).toBe(true);
});
it('detects typeorm query failed errors with username constraint text', () => {
expect(isDuplicateUsernameError({
name: 'QueryFailedError',
message: 'SQLITE_CONSTRAINT: UNIQUE constraint failed: users.username'
})).toBe(true);
});
it('ignores unrelated database errors', () => {
expect(isDuplicateUsernameError({
message: 'UNIQUE constraint failed: servers.id'
})).toBe(false);
expect(isDuplicateUsernameError(new Error('connection lost'))).toBe(false);
expect(isDuplicateUsernameError(null)).toBe(false);
});
});

View File

@@ -0,0 +1,11 @@
export function isDuplicateUsernameError(error: unknown): boolean {
if (!error || typeof error !== 'object') {
return false;
}
const message = 'message' in error && typeof error.message === 'string'
? error.message
: '';
return message.includes('UNIQUE constraint failed: users.username');
}

View File

@@ -10,6 +10,7 @@ import {
import { hashPasswordForStorage, verifyPassword } from '../services/password-auth.service';
import { issueSessionToken, revokeSessionToken } from '../services/session-auth.service';
import { getAuthenticatedUserId, requireAuth } from '../middleware/require-auth';
import { isDuplicateUsernameError } from './user-registration.rules';
const router = Router();
@@ -46,7 +47,16 @@ router.post('/register', async (req, res) => {
createdAt: Date.now()
};
await registerUser(user);
try {
await registerUser(user);
} catch (error) {
if (isDuplicateUsernameError(error)) {
return res.status(409).json({ error: 'Username taken' });
}
throw error;
}
const session = await issueSessionToken(user.id);
res.status(201).json(buildAuthResponse(user, session.token, session.expiresAt));

View File

@@ -7,7 +7,11 @@ import {
import { WebSocket } from 'ws';
import { connectedUsers } from './state';
import { ConnectedUser } from './types';
import { broadcastToServer, findUserByOderId, findVoiceActiveConnection } from './broadcast';
import {
broadcastToServer,
findUserByOderId,
findVoiceActiveConnection
} from './broadcast';
function createMockWs(): WebSocket & { sentMessages: string[] } {
const sent: string[] = [];

View File

@@ -134,12 +134,14 @@ describe('server websocket handler - multi-client sessions', () => {
oderId: 'user-2',
serverIds: new Set(['server-1'])
});
createConnectedUser('conn-passive', {
authenticated: true,
oderId: 'user-1',
serverIds: new Set(['server-1']),
clientInstanceId: 'device-passive'
});
const active = createConnectedUser('conn-active', {
authenticated: true,
oderId: 'user-1',
@@ -169,6 +171,7 @@ describe('server websocket handler - multi-client sessions', () => {
serverIds: new Set(['server-1']),
clientInstanceId: 'device-b'
});
const active = createConnectedUser('conn-active', {
authenticated: true,
oderId: 'user-1',
@@ -197,6 +200,7 @@ describe('server websocket handler - multi-client sessions', () => {
connectionScope: 'ws://localhost:3001',
clientInstanceId: 'device-a'
});
createConnectedUser('conn-new', {
authenticated: false,
connectionScope: 'ws://localhost:3001',