Merge branch 'main' of ssh://tsschulz.de:/home/git/trainingstagebuch
This commit is contained in:
@@ -116,3 +116,15 @@ const deleteTagFromDiaryDate = async (req, res) => {
|
||||
|
||||
export { getDatesForClub, createDateForClub, updateTrainingTimes, addDiaryNote, deleteDiaryNote, addDiaryTag,
|
||||
addTagToDiaryDate, deleteTagFromDiaryDate };
|
||||
|
||||
export const deleteDateForClub = async (req, res) => {
|
||||
try {
|
||||
const { clubId, dateId } = req.params;
|
||||
const { authcode: userToken } = req.headers;
|
||||
const result = await diaryService.removeDateForClub(userToken, clubId, dateId);
|
||||
res.status(200).json(result);
|
||||
} catch (error) {
|
||||
console.error('[deleteDateForClub] - Error:', error);
|
||||
res.status(error.statusCode || 500).json({ error: error.message || 'systemerror' });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"postinstall": "cd ../frontend && npm install && npm run build",
|
||||
"dev": "nodemon server.js"
|
||||
"dev": "nodemon server.js",
|
||||
"cleanup:usertoken": "node ./scripts/cleanupUserTokenKeys.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
deleteDiaryNote,
|
||||
addDiaryTag,
|
||||
addTagToDiaryDate,
|
||||
deleteTagFromDiaryDate
|
||||
deleteTagFromDiaryDate,
|
||||
deleteDateForClub,
|
||||
} from '../controllers/diaryController.js';
|
||||
|
||||
const router = express.Router();
|
||||
@@ -21,5 +22,6 @@ router.delete('/:clubId/tag', authenticate, deleteTagFromDiaryDate);
|
||||
router.get('/:clubId', authenticate, getDatesForClub);
|
||||
router.post('/:clubId', authenticate, createDateForClub);
|
||||
router.put('/:clubId', authenticate, updateTrainingTimes);
|
||||
router.delete('/:clubId/:dateId', authenticate, deleteDateForClub);
|
||||
|
||||
export default router;
|
||||
|
||||
90
backend/scripts/cleanupUserTokenKeys.js
Normal file
90
backend/scripts/cleanupUserTokenKeys.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import mysql from 'mysql2/promise';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const dbConfig = {
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
user: process.env.DB_USER || 'root',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
database: process.env.DB_NAME || 'trainingdiary',
|
||||
};
|
||||
|
||||
async function getIndexSummary(connection, table) {
|
||||
const [rows] = await connection.execute(`SHOW INDEX FROM \`${table}\``);
|
||||
const summary = rows.reduce((acc, r) => {
|
||||
const key = r.Key_name;
|
||||
acc[key] = acc[key] || { unique: r.Non_unique === 0, columns: [] };
|
||||
acc[key].columns.push(r.Column_name);
|
||||
return acc;
|
||||
}, {});
|
||||
return summary;
|
||||
}
|
||||
|
||||
async function cleanupUserTokenKeys() {
|
||||
let connection;
|
||||
const table = 'UserToken';
|
||||
|
||||
try {
|
||||
console.log('Connecting to DB:', dbConfig);
|
||||
connection = await mysql.createConnection(dbConfig);
|
||||
|
||||
console.log(`\nBefore cleanup (indexes on ${table}):`);
|
||||
let before = await getIndexSummary(connection, table);
|
||||
Object.entries(before).forEach(([name, info]) => {
|
||||
console.log(` - ${name} ${info.unique ? '(UNIQUE)' : ''} -> [${info.columns.join(', ')}]`);
|
||||
});
|
||||
|
||||
// Drop all non-PRIMARY indexes on UserToken
|
||||
const [indexes] = await connection.execute(`SHOW INDEX FROM \`${table}\``);
|
||||
const keyNames = Array.from(new Set(indexes.map(i => i.Key_name))).filter(k => k !== 'PRIMARY');
|
||||
|
||||
for (const keyName of keyNames) {
|
||||
try {
|
||||
await connection.execute(`DROP INDEX \`${keyName}\` ON \`${table}\``);
|
||||
console.log(`Dropped index: ${keyName}`);
|
||||
} catch (err) {
|
||||
console.warn(`Could not drop ${keyName}: ${err.code || err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-create minimal, deterministic indexes
|
||||
// Unique on token (column is 'token')
|
||||
try {
|
||||
await connection.execute(`CREATE UNIQUE INDEX \`uniq_UserToken_token\` ON \`${table}\` (\`token\`)`);
|
||||
console.log('Created UNIQUE index: uniq_UserToken_token (token)');
|
||||
} catch (err) {
|
||||
console.warn('Could not create uniq_UserToken_token:', err.code || err.message);
|
||||
}
|
||||
|
||||
// Helpful index on user_id if column exists
|
||||
try {
|
||||
const [cols] = await connection.execute(`SHOW COLUMNS FROM \`${table}\` LIKE 'user_id'`);
|
||||
if (cols && cols.length > 0) {
|
||||
await connection.execute(`CREATE INDEX \`idx_UserToken_user_id\` ON \`${table}\` (\`user_id\`)`);
|
||||
console.log('Created INDEX: idx_UserToken_user_id (user_id)');
|
||||
} else {
|
||||
console.log('Column user_id not found, skip creating idx_UserToken_user_id');
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Could not create idx_UserToken_user_id:', err.code || err.message);
|
||||
}
|
||||
|
||||
console.log(`\nAfter cleanup (indexes on ${table}):`);
|
||||
const after = await getIndexSummary(connection, table);
|
||||
Object.entries(after).forEach(([name, info]) => {
|
||||
console.log(` - ${name} ${info.unique ? '(UNIQUE)' : ''} -> [${info.columns.join(', ')}]`);
|
||||
});
|
||||
|
||||
console.log('\nDone.');
|
||||
} catch (err) {
|
||||
console.error('Cleanup failed:', err);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
if (connection) await connection.end();
|
||||
}
|
||||
}
|
||||
|
||||
cleanupUserTokenKeys();
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ app.get('*', (req, res) => {
|
||||
await TournamentMatch.sync({ alter: true });
|
||||
await TournamentResult.sync({ alter: true });
|
||||
await Accident.sync({ alter: true });
|
||||
await UserToken.sync({ alter: true });
|
||||
await UserToken.sync();
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is running on http://localhost:${port}`);
|
||||
|
||||
@@ -20,7 +20,7 @@ class DiaryDateActivityService {
|
||||
duration: data.duration
|
||||
});
|
||||
}
|
||||
restData.predefinedActivityId = predefinedActivity.id;
|
||||
restData.predefinedActivityId = predefinedActivity.id;
|
||||
const maxOrderId = await DiaryDateActivity.max('orderId', {
|
||||
where: { diaryDateId: data.diaryDateId }
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import DiaryDate from '../models/DiaryDates.js';
|
||||
import DiaryDateActivity from '../models/DiaryDateActivity.js';
|
||||
import Club from '../models/Club.js';
|
||||
import DiaryNote from '../models/DiaryNote.js';
|
||||
import { DiaryTag } from '../models/DiaryTag.js';
|
||||
@@ -151,6 +152,23 @@ class DiaryService {
|
||||
await DiaryDateTag.destroy({ where: { tagId } });
|
||||
}
|
||||
|
||||
async removeDateForClub(userToken, clubId, dateId) {
|
||||
console.log('[DiaryService::removeDateForClub] - Check user access');
|
||||
await checkAccess(userToken, clubId);
|
||||
console.log('[DiaryService::removeDateForClub] - Validate date');
|
||||
const diaryDate = await DiaryDate.findOne({ where: { id: dateId, clubId } });
|
||||
if (!diaryDate) {
|
||||
throw new HttpError('Diary entry not found', 404);
|
||||
}
|
||||
console.log('[DiaryService::removeDateForClub] - Check for activities');
|
||||
const activityCount = await DiaryDateActivity.count({ where: { diaryDateId: dateId } });
|
||||
if (activityCount > 0) {
|
||||
throw new HttpError('Cannot delete date with activities', 409);
|
||||
}
|
||||
console.log('[DiaryService::removeDateForClub] - Delete diary date');
|
||||
await diaryDate.destroy();
|
||||
return { ok: true };
|
||||
}
|
||||
}
|
||||
|
||||
export default new DiaryService();
|
||||
|
||||
Reference in New Issue
Block a user