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:
107
backend/models/ClickTtAccount.js
Normal file
107
backend/models/ClickTtAccount.js
Normal 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;
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user