Add member transfer configuration and UI enhancements
Introduced MemberTransferConfig model and integrated it into the backend, allowing for the storage and retrieval of member transfer settings. Updated server routes to include member transfer configuration endpoints. Enhanced the frontend with a new MemberTransferDialog component for user interaction, added a dedicated route for member transfer settings, and updated the App.vue to include a link for accessing these settings. Improved the loading state and configuration handling in the dialog for better user experience.
This commit is contained in:
117
backend/models/MemberTransferConfig.js
Normal file
117
backend/models/MemberTransferConfig.js
Normal file
@@ -0,0 +1,117 @@
|
||||
import { DataTypes } from 'sequelize';
|
||||
import sequelize from '../database.js';
|
||||
import Club from './Club.js';
|
||||
import { encryptData, decryptData } from '../utils/encrypt.js';
|
||||
|
||||
const MemberTransferConfig = sequelize.define('MemberTransferConfig', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
allowNull: false
|
||||
},
|
||||
clubId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
references: {
|
||||
model: Club,
|
||||
key: 'id'
|
||||
},
|
||||
onDelete: 'CASCADE'
|
||||
},
|
||||
server: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
field: 'server',
|
||||
comment: 'Base URL des Servers (z.B. https://example.com)'
|
||||
},
|
||||
loginEndpoint: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
field: 'login_endpoint',
|
||||
comment: 'Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)'
|
||||
},
|
||||
loginFormat: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: 'json',
|
||||
field: 'login_format'
|
||||
},
|
||||
encryptedLoginCredentials: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
field: 'encrypted_login_credentials',
|
||||
comment: 'Verschlüsselte Login-Daten als JSON-String'
|
||||
},
|
||||
transferEndpoint: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
field: 'transfer_endpoint',
|
||||
comment: 'Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)'
|
||||
},
|
||||
transferMethod: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
defaultValue: 'POST',
|
||||
field: 'transfer_method'
|
||||
},
|
||||
transferFormat: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
defaultValue: 'json',
|
||||
field: 'transfer_format'
|
||||
},
|
||||
transferTemplate: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: false,
|
||||
field: 'transfer_template'
|
||||
},
|
||||
useBulkMode: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
field: 'use_bulk_mode'
|
||||
},
|
||||
bulkWrapperTemplate: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
field: 'bulk_wrapper_template',
|
||||
comment: 'Optionales Template für die äußere Struktur im Bulk-Modus (z.B. {"data": {"members": "{{members}}"}})'
|
||||
}
|
||||
}, {
|
||||
underscored: true,
|
||||
tableName: 'member_transfer_config',
|
||||
timestamps: true
|
||||
});
|
||||
|
||||
// Getter/Setter für verschlüsselte Login-Daten
|
||||
MemberTransferConfig.prototype.getLoginCredentials = function() {
|
||||
if (!this.encryptedLoginCredentials) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const decrypted = decryptData(this.encryptedLoginCredentials);
|
||||
return JSON.parse(decrypted);
|
||||
} catch (error) {
|
||||
console.error('[MemberTransferConfig] Error decrypting login credentials:', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
MemberTransferConfig.prototype.setLoginCredentials = function(credentials) {
|
||||
if (!credentials || Object.keys(credentials).length === 0) {
|
||||
this.encryptedLoginCredentials = null;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const jsonString = JSON.stringify(credentials);
|
||||
this.encryptedLoginCredentials = encryptData(jsonString);
|
||||
} catch (error) {
|
||||
console.error('[MemberTransferConfig] Error encrypting login credentials:', error);
|
||||
this.encryptedLoginCredentials = null;
|
||||
}
|
||||
};
|
||||
|
||||
export default MemberTransferConfig;
|
||||
|
||||
@@ -39,6 +39,7 @@ import MyTischtennis from './MyTischtennis.js';
|
||||
import MyTischtennisUpdateHistory from './MyTischtennisUpdateHistory.js';
|
||||
import MyTischtennisFetchLog from './MyTischtennisFetchLog.js';
|
||||
import ApiLog from './ApiLog.js';
|
||||
import MemberTransferConfig from './MemberTransferConfig.js';
|
||||
// Official tournaments relations
|
||||
OfficialTournament.hasMany(OfficialCompetition, { foreignKey: 'tournamentId', as: 'competitions' });
|
||||
OfficialCompetition.belongsTo(OfficialTournament, { foreignKey: 'tournamentId', as: 'tournament' });
|
||||
@@ -242,6 +243,9 @@ MyTischtennisFetchLog.belongsTo(User, { foreignKey: 'userId', as: 'user' });
|
||||
User.hasMany(ApiLog, { foreignKey: 'userId', as: 'apiLogs' });
|
||||
ApiLog.belongsTo(User, { foreignKey: 'userId', as: 'user' });
|
||||
|
||||
Club.hasOne(MemberTransferConfig, { foreignKey: 'clubId', as: 'memberTransferConfig' });
|
||||
MemberTransferConfig.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
|
||||
|
||||
export {
|
||||
User,
|
||||
Log,
|
||||
@@ -283,4 +287,5 @@ export {
|
||||
MyTischtennisUpdateHistory,
|
||||
MyTischtennisFetchLog,
|
||||
ApiLog,
|
||||
MemberTransferConfig,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user