feat(server, models, services, frontend): integrate Click-TT account functionality

- Added ClickTtAccount model and integrated it into the server and database synchronization processes.
- Updated ClickTtPlayerRegistrationService to utilize ClickTtAccount for user account management, enhancing the registration flow.
- Modified frontend components to include navigation to Click-TT account settings and updated routing to support the new account view.
- Improved error handling and user feedback in the registration process, ensuring clarity in account-related operations.
This commit is contained in:
Torsten Schulz (local)
2026-03-11 15:47:58 +01:00
parent 2ddb63b932
commit 7196fae28e
11 changed files with 780 additions and 13 deletions

View File

@@ -0,0 +1,107 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
import { encryptData, decryptData } from '../utils/encrypt.js';
const ClickTtAccount = sequelize.define('ClickTtAccount', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
userId: {
type: DataTypes.INTEGER,
allowNull: false,
unique: true,
references: {
model: 'user',
key: 'id'
},
onDelete: 'CASCADE',
field: 'user_id'
},
username: {
type: DataTypes.STRING,
allowNull: false,
set(value) {
this.setDataValue('username', encryptData(value));
},
get() {
const encryptedValue = this.getDataValue('username');
return encryptedValue ? decryptData(encryptedValue) : null;
}
},
encryptedPassword: {
type: DataTypes.TEXT('long'),
allowNull: true,
field: 'encrypted_password'
},
savePassword: {
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false,
field: 'save_password'
},
lastLoginAttempt: {
type: DataTypes.DATE,
allowNull: true,
field: 'last_login_attempt'
},
lastLoginSuccess: {
type: DataTypes.DATE,
allowNull: true,
field: 'last_login_success'
},
playwrightStorageState: {
type: DataTypes.TEXT('long'),
allowNull: true,
field: 'playwright_storage_state',
set(value) {
if (value === null || value === undefined) {
this.setDataValue('playwrightStorageState', null);
} else {
const jsonString = typeof value === 'string' ? value : JSON.stringify(value);
this.setDataValue('playwrightStorageState', encryptData(jsonString));
}
},
get() {
const encrypted = this.getDataValue('playwrightStorageState');
if (!encrypted) return null;
try {
return JSON.parse(decryptData(encrypted));
} catch (_err) {
return null;
}
}
}
}, {
underscored: true,
tableName: 'click_tt_account',
timestamps: true,
hooks: {
beforeSave: async (instance) => {
if (!instance.savePassword) {
instance.encryptedPassword = null;
}
}
}
});
ClickTtAccount.prototype.setPassword = function(password) {
if (password && this.savePassword) {
this.encryptedPassword = encryptData(password);
} else {
this.encryptedPassword = null;
}
};
ClickTtAccount.prototype.getPassword = function() {
if (!this.encryptedPassword) return null;
try {
return decryptData(this.encryptedPassword);
} catch (_err) {
return null;
}
};
export default ClickTtAccount;

View File

@@ -41,6 +41,7 @@ import OfficialTournament from './OfficialTournament.js';
import OfficialCompetition from './OfficialCompetition.js';
import OfficialCompetitionMember from './OfficialCompetitionMember.js';
import MyTischtennis from './MyTischtennis.js';
import ClickTtAccount from './ClickTtAccount.js';
import MyTischtennisUpdateHistory from './MyTischtennisUpdateHistory.js';
import MyTischtennisFetchLog from './MyTischtennisFetchLog.js';
import ApiLog from './ApiLog.js';
@@ -322,6 +323,8 @@ DiaryDate.hasMany(Accident, { foreignKey: 'diaryDateId', as: 'accidents' });
User.hasOne(MyTischtennis, { foreignKey: 'userId', as: 'myTischtennis' });
MyTischtennis.belongsTo(User, { foreignKey: 'userId', as: 'user' });
User.hasOne(ClickTtAccount, { foreignKey: 'userId', as: 'clickTtAccount' });
ClickTtAccount.belongsTo(User, { foreignKey: 'userId', as: 'user' });
User.hasMany(MyTischtennisUpdateHistory, { foreignKey: 'userId', as: 'updateHistory' });
MyTischtennisUpdateHistory.belongsTo(User, { foreignKey: 'userId', as: 'user' });
@@ -407,6 +410,7 @@ export {
OfficialCompetition,
OfficialCompetitionMember,
MyTischtennis,
ClickTtAccount,
MyTischtennisUpdateHistory,
MyTischtennisFetchLog,
ApiLog,