Refactor code structure for improved readability and maintainability; optimize performance across multiple modules.
All checks were successful
Deploy to production / deploy (push) Successful in 2m56s

This commit is contained in:
Torsten Schulz (local)
2026-07-21 09:08:15 +02:00
parent 75b52de282
commit 1ab9407e79
1400 changed files with 22278 additions and 485 deletions

0
backend/README_SCHEMA_UPDATES.md Normal file → Executable file
View File

0
backend/README_TAX.md Normal file → Executable file
View File

2
backend/app.js Normal file → Executable file
View File

@@ -24,6 +24,7 @@ import dashboardRouter from './routers/dashboardRouter.js';
import newsRouter from './routers/newsRouter.js';
import calendarRouter from './routers/calendarRouter.js';
import moderationRouter from './routers/moderationRouter.js';
import pushNotificationRouter from './routers/pushNotificationRouter.js';
import cors from 'cors';
import './jobs/sessionCleanup.js';
@@ -107,6 +108,7 @@ app.use('/api/socialnetwork', socialnetworkRouter);
app.use('/api/vocab', vocabRouter);
app.use('/api/forum', forumRouter);
app.use('/api/moderation', moderationRouter);
app.use('/api/push', pushNotificationRouter);
app.use('/api/falukant', falukantRouter);
app.use('/api/friendships', friendshipRouter);
app.use('/api/models', modelsProxyRouter);

0
backend/check-connections.js Normal file → Executable file
View File

0
backend/cleanup-connections.js Normal file → Executable file
View File

0
backend/config/chatBridge.json Normal file → Executable file
View File

0
backend/config/config.json Normal file → Executable file
View File

0
backend/config/loadEnv.js Normal file → Executable file
View File

0
backend/config/sequelize-cli.cjs Normal file → Executable file
View File

0
backend/controllers/adminController.js Normal file → Executable file
View File

3
backend/controllers/authController.js Normal file → Executable file
View File

@@ -63,7 +63,8 @@ class AuthController {
async oauthStart(req, res) {
const { provider } = req.params;
try {
const redirectTo = await oauthService.startOAuthLogin({ providerSlug: provider });
const client = req.query.client === 'android' ? 'android' : 'web';
const redirectTo = await oauthService.startOAuthLogin({ providerSlug: provider, client });
res.redirect(302, redirectTo.toString());
} catch (error) {
const status = error.message === 'providernotconfigured' ? 503 : 500;

0
backend/controllers/blogController.js Normal file → Executable file
View File

0
backend/controllers/calendarController.js Normal file → Executable file
View File

0
backend/controllers/chatController.js Normal file → Executable file
View File

0
backend/controllers/contactController.js Normal file → Executable file
View File

0
backend/controllers/dashboardController.js Normal file → Executable file
View File

0
backend/controllers/falukantController.js Normal file → Executable file
View File

0
backend/controllers/forumController.js Normal file → Executable file
View File

0
backend/controllers/friendshipController.js Normal file → Executable file
View File

0
backend/controllers/match3Controller.js Normal file → Executable file
View File

0
backend/controllers/minigamesController.js Normal file → Executable file
View File

0
backend/controllers/moderationController.js Normal file → Executable file
View File

0
backend/controllers/navigationController.js Normal file → Executable file
View File

0
backend/controllers/newsController.js Normal file → Executable file
View File

View File

@@ -0,0 +1,34 @@
import Joi from 'joi';
import * as pushNotificationService from '../services/pushNotificationService.js';
const tokenSchema = Joi.object({ token: Joi.string().trim().min(20).max(4096).required(), enabled: Joi.boolean().default(true) });
const settingsSchema = Joi.object({
enabled: Joi.boolean(),
chatEnabled: Joi.boolean(),
friendLoginEnabled: Joi.boolean(),
falukantEnabled: Joi.boolean(),
vocabReminderEnabled: Joi.boolean(),
}).min(1);
const pushNotificationController = {
async registerDevice(req, res) {
const { error, value } = tokenSchema.validate(req.body || {});
if (error) return res.status(400).json({ error: error.details[0].message });
return res.status(200).json(await pushNotificationService.registerDevice(req.headers.userid, value));
},
async disableDevice(req, res) {
const { error, value } = tokenSchema.validate({ token: req.params.token, enabled: false });
if (error) return res.status(400).json({ error: error.details[0].message });
return res.status(200).json(await pushNotificationService.disableDevice(req.headers.userid, value.token));
},
async getSettings(req, res) {
return res.status(200).json(await pushNotificationService.loadSettings(req.headers.userid));
},
async updateSettings(req, res) {
const { error, value } = settingsSchema.validate(req.body || {});
if (error) return res.status(400).json({ error: error.details[0].message });
return res.status(200).json(await pushNotificationService.updateSettings(req.headers.userid, value));
},
};
export default pushNotificationController;

6
backend/controllers/settingsController.js Normal file → Executable file
View File

@@ -68,6 +68,9 @@ class SettingsController {
async getAccountSettings(req, res) {
try {
const { userId } = req.body;
if (userId !== req.headers.userid) {
return res.status(403).json({ error: 'Access denied' });
}
const accountSettings = await settingsService.getAccountSettings(userId);
res.status(200).json(accountSettings);
} catch (error) {
@@ -86,6 +89,9 @@ class SettingsController {
return res.status(400).json({ error: error.details[0].message });
}
try {
if (value.userId !== req.headers.userid) {
return res.status(403).json({ error: 'Access denied' });
}
await settingsService.setAccountSettings(value);
res.status(200).json({ message: 'Account settings updated successfully' });
} catch (error) {

12
backend/controllers/socialnetworkController.js Normal file → Executable file
View File

@@ -40,6 +40,8 @@ class SocialNetworkController {
this.removeFriend = this.removeFriend.bind(this);
this.acceptFriendship = this.acceptFriendship.bind(this);
this.getLoggedInFriends = this.getLoggedInFriends.bind(this);
this.blockUser = this.blockUser.bind(this);
this.unblockUser = this.unblockUser.bind(this);
}
async userSearch(req, res) {
@@ -54,6 +56,16 @@ class SocialNetworkController {
}
}
async blockUser(req, res) {
try { await this.socialNetworkService.blockUser(req.headers.userid, req.params.userId); res.status(204).send(); }
catch (error) { res.status(400).json({ error: error.message }); }
}
async unblockUser(req, res) {
try { await this.socialNetworkService.unblockUser(req.headers.userid, req.params.userId); res.status(204).send(); }
catch (error) { res.status(400).json({ error: error.message }); }
}
async profile(req, res) {
try {
const { userId } = req.params;

0
backend/controllers/taxiController.js Normal file → Executable file
View File

0
backend/controllers/taxiHighscoreController.js Normal file → Executable file
View File

0
backend/controllers/taxiMapController.js Normal file → Executable file
View File

0
backend/controllers/termineController.js Normal file → Executable file
View File

0
backend/controllers/vocabController.js Normal file → Executable file
View File

0
backend/daemonServer.js Normal file → Executable file
View File

0
backend/data/lesson-checkpoints.json Normal file → Executable file
View File

0
backend/data/termine.csv Normal file → Executable file
View File

Can't render this file because it contains an unexpected character in line 4 and column 91.

0
backend/env.example Normal file → Executable file
View File

0
backend/env.local.example Normal file → Executable file
View File

0
backend/fix-index-fields-v2.js Normal file → Executable file
View File

0
backend/fix-index-fields.js Normal file → Executable file
View File

0
backend/fix-pgcrypto-extension.js Normal file → Executable file
View File

0
backend/jobs/politicalBenefitsTick.js Normal file → Executable file
View File

0
backend/jobs/sessionCleanup.js Normal file → Executable file
View File

0
backend/locales/de.json Normal file → Executable file
View File

0
backend/locales/en.json Normal file → Executable file
View File

0
backend/middleware/authMiddleware.js Normal file → Executable file
View File

View File

@@ -0,0 +1,33 @@
import multer from 'multer';
const imageMimeTypes = new Set(['image/jpeg', 'image/png', 'image/webp', 'image/gif']);
const verificationMimeTypes = new Set([...imageMimeTypes, 'application/pdf']);
const videoMimeTypes = new Set(['video/mp4', 'video/webm', 'video/ogg', 'video/quicktime']);
function createUpload({ maxBytes, allowedMimeTypes }) {
return multer({
storage: multer.memoryStorage(),
limits: { fileSize: maxBytes, files: 1 },
fileFilter: (_req, file, callback) => {
if (!allowedMimeTypes.has(file.mimetype)) {
callback(new multer.MulterError('LIMIT_UNEXPECTED_FILE', file.fieldname));
return;
}
callback(null, true);
},
});
}
export const imageUpload = createUpload({ maxBytes: 10 * 1024 * 1024, allowedMimeTypes: imageMimeTypes });
export const adultVerificationUpload = createUpload({ maxBytes: 10 * 1024 * 1024, allowedMimeTypes: verificationMimeTypes });
export const videoUpload = createUpload({ maxBytes: 100 * 1024 * 1024, allowedMimeTypes: videoMimeTypes });
export function uploadErrorHandler(error, _req, res, next) {
if (error instanceof multer.MulterError) {
const message = error.code === 'LIMIT_FILE_SIZE'
? 'Die hochgeladene Datei ist zu groß.'
: 'Der Dateityp ist nicht erlaubt.';
return res.status(400).json({ error: message });
}
return next(error);
}

0
backend/migrations-active/.gitkeep Normal file → Executable file
View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

@@ -0,0 +1,16 @@
seiteBEGIN;
CREATE TABLE IF NOT EXISTS community.user_block (
id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
blocker_id INTEGER NOT NULL REFERENCES community."user"(id) ON DELETE CASCADE,
blocked_id INTEGER NOT NULL REFERENCES community."user"(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT user_block_not_self CHECK (blocker_id <> blocked_id),
CONSTRAINT user_block_unique UNIQUE (blocker_id, blocked_id)
);
CREATE INDEX IF NOT EXISTS user_block_blocker_id_idx
ON community.user_block (blocker_id);
COMMIT;

View File

@@ -0,0 +1,24 @@
CREATE TABLE IF NOT EXISTS community.push_notification_device (
id BIGSERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES community."user"(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE,
platform VARCHAR(20) NOT NULL DEFAULT 'android',
enabled BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT push_notification_device_platform_check CHECK (platform IN ('android'))
);
CREATE INDEX IF NOT EXISTS push_notification_device_user_enabled_idx
ON community.push_notification_device (user_id, enabled);
CREATE TABLE IF NOT EXISTS community.push_notification_setting (
user_id INTEGER PRIMARY KEY REFERENCES community."user"(id) ON DELETE CASCADE,
enabled BOOLEAN NOT NULL DEFAULT FALSE,
chat_enabled BOOLEAN NOT NULL DEFAULT TRUE,
friend_login_enabled BOOLEAN NOT NULL DEFAULT TRUE,
falukant_enabled BOOLEAN NOT NULL DEFAULT TRUE,
vocab_reminder_enabled BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

0
backend/migrations/README.md Normal file → Executable file
View File

0
backend/migrations/add_chat_room_dialog_fields.sql Normal file → Executable file
View File

0
backend/migrations/add_condition_to_vehicle.sql Normal file → Executable file
View File

Some files were not shown because too many files have changed in this diff Show More