Füge Import- und Exportfunktionen für Gottesdienste hinzu: Implementiere die Möglichkeit, Gottesdienste aus .doc und .docx-Dateien zu importieren und in verschiedenen Formaten zu exportieren. Verbessere die Benutzeroberfläche des Worship Management-Formulars mit neuen Schaltflächen für Import und Export sowie Dialogen zur Bearbeitung importierter Daten. Aktualisiere die Datenbankstruktur, um neue Felder für die Genehmigung und das Orgelspiel zu unterstützen.
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -27,4 +27,5 @@ server.key
|
|||||||
server.cert
|
server.cert
|
||||||
|
|
||||||
public/images/uploads/1ba24ea7-f52c-4179-896f-1909269cab58.jpg
|
public/images/uploads/1ba24ea7-f52c-4179-896f-1909269cab58.jpg
|
||||||
actualize.sh
|
actualize.sh
|
||||||
|
files/uploads/GD 24.08.2025-04.01.2026 Stand 12.08.2025.docx
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const { Sequelize } = require('sequelize');
|
const { Sequelize } = require('sequelize');
|
||||||
|
|
||||||
const sequelize = new Sequelize('miriamgemeinde', 'miriam_user', 'qTCTTWwpEwy3vPDU', {
|
const sequelize = new Sequelize('miriamgemeinde', 'miriamgemeinde', 'hitomisan', {
|
||||||
host: 'tsschulz.de',
|
host: 'localhost',
|
||||||
dialect: 'mysql',
|
dialect: 'mysql',
|
||||||
retry: {
|
retry: {
|
||||||
match: [
|
match: [
|
||||||
@@ -26,7 +26,7 @@ const sequelize = new Sequelize('miriamgemeinde', 'miriam_user', 'qTCTTWwpEwy3vP
|
|||||||
async function connectWithRetry() {
|
async function connectWithRetry() {
|
||||||
try {
|
try {
|
||||||
await sequelize.authenticate();
|
await sequelize.authenticate();
|
||||||
console.log('Connection has been established successfully.');
|
console.log(`Connection has been established successfully. Database server: ${sequelize.config.host}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Unable to connect to the database:', error);
|
console.error('Unable to connect to the database:', error);
|
||||||
setTimeout(connectWithRetry, 5000);
|
setTimeout(connectWithRetry, 5000);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
16
migrations/20251122134324-add-orgelspiel-to-worships.js
Normal file
16
migrations/20251122134324-add-orgelspiel-to-worships.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/** @type {import('sequelize-cli').Migration} */
|
||||||
|
module.exports = {
|
||||||
|
up: async (queryInterface, Sequelize) => {
|
||||||
|
await queryInterface.addColumn('worships', 'organ_playing', {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
down: async (queryInterface, Sequelize) => {
|
||||||
|
await queryInterface.removeColumn('worships', 'organ_playing');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
17
migrations/20251122140000-add-freigegeben-to-worships.js
Normal file
17
migrations/20251122140000-add-freigegeben-to-worships.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/** @type {import('sequelize-cli').Migration} */
|
||||||
|
module.exports = {
|
||||||
|
up: async (queryInterface, Sequelize) => {
|
||||||
|
await queryInterface.addColumn('worships', 'approved', {
|
||||||
|
type: Sequelize.BOOLEAN,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
down: async (queryInterface, Sequelize) => {
|
||||||
|
await queryInterface.removeColumn('worships', 'approved');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@@ -61,6 +61,17 @@ module.exports = (sequelize) => {
|
|||||||
allowNull: true,
|
allowNull: true,
|
||||||
field: 'sacristan_service'
|
field: 'sacristan_service'
|
||||||
},
|
},
|
||||||
|
organPlaying: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
field: 'organ_playing'
|
||||||
|
},
|
||||||
|
approved: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
defaultValue: false,
|
||||||
|
allowNull: false,
|
||||||
|
field: 'approved'
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
tableName: 'worships',
|
tableName: 'worships',
|
||||||
timestamps: true
|
timestamps: true
|
||||||
|
|||||||
369
package-lock.json
generated
369
package-lock.json
generated
@@ -33,10 +33,12 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
|
"docx": "^9.5.1",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"mammoth": "^1.11.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"mysql2": "^3.10.1",
|
"mysql2": "^3.10.1",
|
||||||
@@ -3617,11 +3619,12 @@
|
|||||||
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
|
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.14.8",
|
"version": "24.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
|
||||||
"integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==",
|
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~5.26.4"
|
"undici-types": "~7.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node-forge": {
|
"node_modules/@types/node-forge": {
|
||||||
@@ -4862,6 +4865,15 @@
|
|||||||
"@xtuc/long": "4.2.2"
|
"@xtuc/long": "4.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@xmldom/xmldom": {
|
||||||
|
"version": "0.8.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
|
||||||
|
"integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xtuc/ieee754": {
|
"node_modules/@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||||
@@ -7873,6 +7885,12 @@
|
|||||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
|
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/dingbat-to-unicode": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
"node_modules/dir-glob": {
|
"node_modules/dir-glob": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||||
@@ -7908,6 +7926,41 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/docx": {
|
||||||
|
"version": "9.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/docx/-/docx-9.5.1.tgz",
|
||||||
|
"integrity": "sha512-ABDI7JEirFD2+bHhOBlsGZxaG1UgZb2M/QMKhLSDGgVNhxDesTCDcP+qoDnDGjZ4EOXTRfUjUgwHVuZ6VSTfWQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "^24.0.1",
|
||||||
|
"hash.js": "^1.1.7",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
|
"nanoid": "^5.1.3",
|
||||||
|
"xml": "^1.0.1",
|
||||||
|
"xml-js": "^1.6.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/docx/node_modules/nanoid": {
|
||||||
|
"version": "5.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz",
|
||||||
|
"integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18 || >=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dom-converter": {
|
"node_modules/dom-converter": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
|
||||||
@@ -8056,6 +8109,15 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/duck": {
|
||||||
|
"version": "0.1.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/duck/-/duck-0.1.12.tgz",
|
||||||
|
"integrity": "sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==",
|
||||||
|
"license": "BSD",
|
||||||
|
"dependencies": {
|
||||||
|
"underscore": "^1.13.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dunder-proto": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
@@ -10457,7 +10519,6 @@
|
|||||||
"version": "1.1.7",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
||||||
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
"minimalistic-assert": "^1.0.1"
|
"minimalistic-assert": "^1.0.1"
|
||||||
@@ -10795,6 +10856,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
||||||
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="
|
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/immediate": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
@@ -11867,6 +11934,18 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jszip": {
|
||||||
|
"version": "3.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||||
|
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||||
|
"license": "(MIT OR GPL-3.0-or-later)",
|
||||||
|
"dependencies": {
|
||||||
|
"lie": "~3.3.0",
|
||||||
|
"pako": "~1.0.2",
|
||||||
|
"readable-stream": "~2.3.6",
|
||||||
|
"setimmediate": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jwa": {
|
"node_modules/jwa": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
|
||||||
@@ -11951,6 +12030,15 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lie": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"immediate": "~3.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lilconfig": {
|
"node_modules/lilconfig": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
||||||
@@ -12350,6 +12438,17 @@
|
|||||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
|
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/lop": {
|
||||||
|
"version": "0.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lop/-/lop-0.4.2.tgz",
|
||||||
|
"integrity": "sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"duck": "^0.1.12",
|
||||||
|
"option": "~0.2.1",
|
||||||
|
"underscore": "^1.13.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lowdb": {
|
"node_modules/lowdb": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz",
|
||||||
@@ -12423,6 +12522,54 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mammoth": {
|
||||||
|
"version": "1.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mammoth/-/mammoth-1.11.0.tgz",
|
||||||
|
"integrity": "sha512-BcEqqY/BOwIcI1iR5tqyVlqc3KIaMRa4egSoK83YAVrBf6+yqdAAbtUcFDCWX8Zef8/fgNZ6rl4VUv+vVX8ddQ==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@xmldom/xmldom": "^0.8.6",
|
||||||
|
"argparse": "~1.0.3",
|
||||||
|
"base64-js": "^1.5.1",
|
||||||
|
"bluebird": "~3.4.0",
|
||||||
|
"dingbat-to-unicode": "^1.0.1",
|
||||||
|
"jszip": "^3.7.1",
|
||||||
|
"lop": "^0.4.2",
|
||||||
|
"path-is-absolute": "^1.0.0",
|
||||||
|
"underscore": "^1.13.1",
|
||||||
|
"xmlbuilder": "^10.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mammoth": "bin/mammoth"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mammoth/node_modules/argparse": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sprintf-js": "~1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mammoth/node_modules/bluebird": {
|
||||||
|
"version": "3.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
|
||||||
|
"integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/mammoth/node_modules/xmlbuilder": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/map-cache": {
|
"node_modules/map-cache": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
|
||||||
@@ -12723,8 +12870,7 @@
|
|||||||
"node_modules/minimalistic-assert": {
|
"node_modules/minimalistic-assert": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/minimalistic-crypto-utils": {
|
"node_modules/minimalistic-crypto-utils": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -13464,6 +13610,12 @@
|
|||||||
"opener": "bin/opener-bin.js"
|
"opener": "bin/opener-bin.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/option": {
|
||||||
|
"version": "0.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/option/-/option-0.2.4.tgz",
|
||||||
|
"integrity": "sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
@@ -13687,6 +13839,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
||||||
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
|
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||||
|
"license": "(MIT AND Zlib)"
|
||||||
|
},
|
||||||
"node_modules/param-case": {
|
"node_modules/param-case": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||||
@@ -16130,6 +16288,12 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/setimmediate": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/setprototypeof": {
|
"node_modules/setprototypeof": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
@@ -17694,10 +17858,17 @@
|
|||||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
||||||
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
|
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/underscore": {
|
||||||
|
"version": "1.13.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz",
|
||||||
|
"integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "5.26.5",
|
"version": "7.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unicode-canonical-property-names-ecmascript": {
|
"node_modules/unicode-canonical-property-names-ecmascript": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@@ -19097,6 +19268,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xml": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/xml-js": {
|
||||||
|
"version": "1.6.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
|
||||||
|
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"xml-js": "bin/cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/xml2js": {
|
"node_modules/xml2js": {
|
||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
|
||||||
@@ -21751,11 +21940,11 @@
|
|||||||
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
|
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "20.14.8",
|
"version": "24.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
|
||||||
"integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==",
|
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"undici-types": "~5.26.4"
|
"undici-types": "~7.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node-forge": {
|
"@types/node-forge": {
|
||||||
@@ -22763,6 +22952,11 @@
|
|||||||
"@xtuc/long": "4.2.2"
|
"@xtuc/long": "4.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@xmldom/xmldom": {
|
||||||
|
"version": "0.8.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
|
||||||
|
"integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw=="
|
||||||
|
},
|
||||||
"@xtuc/ieee754": {
|
"@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||||
@@ -24960,6 +25154,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dingbat-to-unicode": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w=="
|
||||||
|
},
|
||||||
"dir-glob": {
|
"dir-glob": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||||
@@ -24986,6 +25185,26 @@
|
|||||||
"esutils": "^2.0.2"
|
"esutils": "^2.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"docx": {
|
||||||
|
"version": "9.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/docx/-/docx-9.5.1.tgz",
|
||||||
|
"integrity": "sha512-ABDI7JEirFD2+bHhOBlsGZxaG1UgZb2M/QMKhLSDGgVNhxDesTCDcP+qoDnDGjZ4EOXTRfUjUgwHVuZ6VSTfWQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "^24.0.1",
|
||||||
|
"hash.js": "^1.1.7",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
|
"nanoid": "^5.1.3",
|
||||||
|
"xml": "^1.0.1",
|
||||||
|
"xml-js": "^1.6.8"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": {
|
||||||
|
"version": "5.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz",
|
||||||
|
"integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dom-converter": {
|
"dom-converter": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
|
||||||
@@ -25105,6 +25324,14 @@
|
|||||||
"rimraf": "^3.0.0"
|
"rimraf": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"duck": {
|
||||||
|
"version": "0.1.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/duck/-/duck-0.1.12.tgz",
|
||||||
|
"integrity": "sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==",
|
||||||
|
"requires": {
|
||||||
|
"underscore": "^1.13.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dunder-proto": {
|
"dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
@@ -26875,7 +27102,6 @@
|
|||||||
"version": "1.1.7",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
||||||
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
"minimalistic-assert": "^1.0.1"
|
"minimalistic-assert": "^1.0.1"
|
||||||
@@ -27108,6 +27334,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
||||||
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="
|
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="
|
||||||
},
|
},
|
||||||
|
"immediate": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||||
|
},
|
||||||
"import-fresh": {
|
"import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
@@ -27876,6 +28107,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jszip": {
|
||||||
|
"version": "3.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||||
|
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||||
|
"requires": {
|
||||||
|
"lie": "~3.3.0",
|
||||||
|
"pako": "~1.0.2",
|
||||||
|
"readable-stream": "~2.3.6",
|
||||||
|
"setimmediate": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"jwa": {
|
"jwa": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
|
||||||
@@ -27948,6 +28190,14 @@
|
|||||||
"type-check": "~0.4.0"
|
"type-check": "~0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lie": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||||
|
"requires": {
|
||||||
|
"immediate": "~3.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"lilconfig": {
|
"lilconfig": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
||||||
@@ -28263,6 +28513,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||||
},
|
},
|
||||||
|
"lop": {
|
||||||
|
"version": "0.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lop/-/lop-0.4.2.tgz",
|
||||||
|
"integrity": "sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw==",
|
||||||
|
"requires": {
|
||||||
|
"duck": "^0.1.12",
|
||||||
|
"option": "~0.2.1",
|
||||||
|
"underscore": "^1.13.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"lowdb": {
|
"lowdb": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz",
|
||||||
@@ -28322,6 +28582,43 @@
|
|||||||
"semver": "^6.0.0"
|
"semver": "^6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mammoth": {
|
||||||
|
"version": "1.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mammoth/-/mammoth-1.11.0.tgz",
|
||||||
|
"integrity": "sha512-BcEqqY/BOwIcI1iR5tqyVlqc3KIaMRa4egSoK83YAVrBf6+yqdAAbtUcFDCWX8Zef8/fgNZ6rl4VUv+vVX8ddQ==",
|
||||||
|
"requires": {
|
||||||
|
"@xmldom/xmldom": "^0.8.6",
|
||||||
|
"argparse": "~1.0.3",
|
||||||
|
"base64-js": "^1.5.1",
|
||||||
|
"bluebird": "~3.4.0",
|
||||||
|
"dingbat-to-unicode": "^1.0.1",
|
||||||
|
"jszip": "^3.7.1",
|
||||||
|
"lop": "^0.4.2",
|
||||||
|
"path-is-absolute": "^1.0.0",
|
||||||
|
"underscore": "^1.13.1",
|
||||||
|
"xmlbuilder": "^10.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
|
"requires": {
|
||||||
|
"sprintf-js": "~1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bluebird": {
|
||||||
|
"version": "3.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
|
||||||
|
"integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA=="
|
||||||
|
},
|
||||||
|
"xmlbuilder": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"map-cache": {
|
"map-cache": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
|
||||||
@@ -28544,8 +28841,7 @@
|
|||||||
"minimalistic-assert": {
|
"minimalistic-assert": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"minimalistic-crypto-utils": {
|
"minimalistic-crypto-utils": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -29088,6 +29384,11 @@
|
|||||||
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
|
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"option": {
|
||||||
|
"version": "0.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/option/-/option-0.2.4.tgz",
|
||||||
|
"integrity": "sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A=="
|
||||||
|
},
|
||||||
"optionator": {
|
"optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
@@ -29247,6 +29548,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
||||||
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
|
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
|
||||||
},
|
},
|
||||||
|
"pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||||
|
},
|
||||||
"param-case": {
|
"param-case": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||||
@@ -31024,6 +31330,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"setimmediate": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
|
||||||
|
},
|
||||||
"setprototypeof": {
|
"setprototypeof": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
@@ -32204,10 +32515,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
||||||
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
|
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
|
||||||
},
|
},
|
||||||
|
"underscore": {
|
||||||
|
"version": "1.13.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz",
|
||||||
|
"integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g=="
|
||||||
|
},
|
||||||
"undici-types": {
|
"undici-types": {
|
||||||
"version": "5.26.5",
|
"version": "7.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="
|
||||||
},
|
},
|
||||||
"unicode-canonical-property-names-ecmascript": {
|
"unicode-canonical-property-names-ecmascript": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@@ -33204,6 +33520,19 @@
|
|||||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
"xml": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="
|
||||||
|
},
|
||||||
|
"xml-js": {
|
||||||
|
"version": "1.6.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
|
||||||
|
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
|
||||||
|
"requires": {
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"xml2js": {
|
"xml2js": {
|
||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
|
||||||
|
|||||||
@@ -34,10 +34,12 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
|
"docx": "^9.5.1",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"mammoth": "^1.11.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"mysql2": "^3.10.1",
|
"mysql2": "^3.10.1",
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const { getAllWorships, createWorship, updateWorship, deleteWorship, getFilteredWorships, getWorshipOptions } = require('../controllers/worshipController');
|
const { getAllWorships, createWorship, updateWorship, deleteWorship, getFilteredWorships, getWorshipOptions, importWorships, uploadImportFile, exportWorships, saveImportedWorships } = require('../controllers/worshipController');
|
||||||
const authMiddleware = require('../middleware/authMiddleware');
|
const authMiddleware = require('../middleware/authMiddleware');
|
||||||
|
|
||||||
router.get('/', getAllWorships);
|
router.get('/', getAllWorships);
|
||||||
router.get('/options', getWorshipOptions);
|
router.get('/options', getWorshipOptions);
|
||||||
router.post('/', authMiddleware, createWorship);
|
router.post('/', authMiddleware, createWorship);
|
||||||
|
router.post('/import', authMiddleware, uploadImportFile, importWorships);
|
||||||
|
router.post('/import/save', authMiddleware, saveImportedWorships);
|
||||||
router.put('/:id', authMiddleware, updateWorship);
|
router.put('/:id', authMiddleware, updateWorship);
|
||||||
router.delete('/:id', authMiddleware, deleteWorship);
|
router.delete('/:id', authMiddleware, deleteWorship);
|
||||||
router.get('/filtered', getFilteredWorships);
|
router.get('/filtered', getFilteredWorships);
|
||||||
|
router.get('/export', authMiddleware, exportWorships);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
66
server.js
66
server.js
@@ -2,8 +2,14 @@ const express = require('express');
|
|||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
|
const http = require('http');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
|
||||||
|
// Erhöhe maxHttpHeaderSize für Node.js (Standard ist 8KB, erhöhe auf 16KB)
|
||||||
|
if (http.maxHeaderSize !== undefined) {
|
||||||
|
http.maxHeaderSize = 16384;
|
||||||
|
}
|
||||||
const sequelize = require('./config/database');
|
const sequelize = require('./config/database');
|
||||||
const authRouter = require('./routes/auth');
|
const authRouter = require('./routes/auth');
|
||||||
const eventTypesRouter = require('./routes/eventtypes');
|
const eventTypesRouter = require('./routes/eventtypes');
|
||||||
@@ -31,9 +37,50 @@ const allowedOrigins = (process.env.ALLOWED_ORIGINS || '')
|
|||||||
|
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
origin: (origin, callback) => {
|
origin: (origin, callback) => {
|
||||||
if (!origin) return callback(null, true); // z.B. Healthchecks/curl/Server-zu-Server
|
if (!origin) {
|
||||||
if (allowedOrigins.length === 0) return callback(null, true); // Fallback: alles erlauben
|
return callback(null, true); // z.B. Healthchecks/curl/Server-zu-Server
|
||||||
if (allowedOrigins.includes(origin)) return callback(null, true);
|
}
|
||||||
|
|
||||||
|
if (allowedOrigins.length === 0) {
|
||||||
|
return callback(null, true); // Fallback: alles erlauben
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe exakte Übereinstimmung
|
||||||
|
if (allowedOrigins.includes(origin)) {
|
||||||
|
return callback(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Für Entwicklung: Erlaube localhost und torstens auf jedem Port
|
||||||
|
try {
|
||||||
|
const originUrl = new URL(origin);
|
||||||
|
const hostname = originUrl.hostname.toLowerCase();
|
||||||
|
const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1';
|
||||||
|
const isTorstens = hostname === 'torstens' || hostname.includes('torstens');
|
||||||
|
|
||||||
|
if (isLocalhost || isTorstens) {
|
||||||
|
return callback(null, true);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Falls URL-Parsing fehlschlägt, prüfe mit Regex
|
||||||
|
const isLocalhost = /^https?:\/\/(localhost|127\.0\.0\.1|::1)(:\d+)?$/.test(origin);
|
||||||
|
const isTorstens = /^https?:\/\/torstens(:\d+)?/.test(origin);
|
||||||
|
|
||||||
|
if (isLocalhost || isTorstens) {
|
||||||
|
return callback(null, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe auch ohne Port (für Flexibilität)
|
||||||
|
const originWithoutPort = origin.replace(/:\d+$/, '');
|
||||||
|
const allowedWithoutPort = allowedOrigins.some(allowed => {
|
||||||
|
const allowedWithoutPort = allowed.replace(/:\d+$/, '');
|
||||||
|
return originWithoutPort === allowedWithoutPort;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (allowedWithoutPort) {
|
||||||
|
return callback(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
return callback(new Error('Not allowed by CORS'), false);
|
return callback(new Error('Not allowed by CORS'), false);
|
||||||
},
|
},
|
||||||
credentials: true,
|
credentials: true,
|
||||||
@@ -42,7 +89,14 @@ app.use(cors({
|
|||||||
}));
|
}));
|
||||||
app.options('*', cors());
|
app.options('*', cors());
|
||||||
|
|
||||||
app.use(bodyParser.json());
|
// Erhöhe Header-Limits für große Requests
|
||||||
|
app.use(bodyParser.json({ limit: '50mb' }));
|
||||||
|
app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));
|
||||||
|
|
||||||
|
// Erhöhe maxHttpHeaderSize (Node.js 18.3.0+)
|
||||||
|
if (process.versions.node.split('.')[0] >= 18) {
|
||||||
|
require('http').maxHeaderSize = 16384; // 16KB (Standard ist 8KB)
|
||||||
|
}
|
||||||
|
|
||||||
app.use('/api/auth', authRouter);
|
app.use('/api/auth', authRouter);
|
||||||
app.use('/api/event-types', eventTypesRouter);
|
app.use('/api/event-types', eventTypesRouter);
|
||||||
@@ -69,7 +123,7 @@ sequelize.sync().then(() => {
|
|||||||
/* https.createServer(options, app).listen(PORT, () => {
|
/* https.createServer(options, app).listen(PORT, () => {
|
||||||
console.log(`Server läuft auf Port ${PORT}`);
|
console.log(`Server läuft auf Port ${PORT}`);
|
||||||
});*/
|
});*/
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, '0.0.0.0', () => {
|
||||||
console.log(`Server läuft auf Port ${PORT}`);
|
console.log(`Server läuft auf Port ${PORT} (IPv4 und IPv6)`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,8 +24,11 @@ axios.interceptors.response.use(
|
|||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
if (error.response && error.response.status === 401) {
|
if (error.response && error.response.status === 401) {
|
||||||
store.dispatch('logout');
|
store.dispatch('logout').then(() => {
|
||||||
router.push('/auth/login');
|
if (router.currentRoute.value.path !== '/auth/login') {
|
||||||
|
router.replace('/auth/login');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,27 +36,53 @@ export default {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.dialog-overlay {
|
.dialog-overlay {
|
||||||
top: calc(50% - 25em);
|
position: fixed;
|
||||||
left: 5%;
|
top: 0;
|
||||||
width: 90%;
|
left: 0;
|
||||||
height: 50em;
|
width: 100%;
|
||||||
background: rgba(0, 0, 0, .5);
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
overflow: auto;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog {
|
.dialog {
|
||||||
background: white;
|
background: white;
|
||||||
padding: 20px;
|
padding: 30px;
|
||||||
border-radius: 5px;
|
border-radius: 8px;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
width: 100%;
|
width: 90%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
.dialog h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #333;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog p {
|
||||||
|
margin: 15px 0;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog button {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #007BFF;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="left-links">
|
<div class="left-links">
|
||||||
<router-link class="login-link" to="/auth/login" v-if="!isLoggedIn">Login</router-link>
|
<router-link class="login-link" to="/auth/login" v-if="!isLoggedIn">Login</router-link>
|
||||||
<a v-if="isLoggedIn" @click="logout" class="logout-link">Logout</a>
|
<a v-if="isLoggedIn" @click="handleLogout" class="logout-link">Logout</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-links">
|
<div class="right-links">
|
||||||
<router-link to="/terms">Impressum</router-link>
|
<router-link to="/terms">Impressum</router-link>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
<div v-if="worship.organizer">Gestaltung: {{ worship.organizer }}</div>
|
<div v-if="worship.organizer">Gestaltung: {{ worship.organizer }}</div>
|
||||||
<div v-if="worship.sacristanService" class="internal-information">Küsterdienst: {{ worship.sacristanService }}</div>
|
<div v-if="worship.sacristanService" class="internal-information">Küsterdienst: {{ worship.sacristanService }}</div>
|
||||||
<div v-if="worship.collection">Kollekte: {{ worship.collection }}</div>
|
<div v-if="worship.collection">Kollekte: {{ worship.collection }}</div>
|
||||||
|
<div v-if="worship.organPlaying" class="internal-information">Orgelspiel: {{ worship.organPlaying }}</div>
|
||||||
<div v-if="worship.address">{{ worship.address }}</div>
|
<div v-if="worship.address">{{ worship.address }}</div>
|
||||||
<div v-if="!worship.address && worship.eventPlace.id && worship.eventPlace.id">
|
<div v-if="!worship.address && worship.eventPlace.id && worship.eventPlace.id">
|
||||||
Adresse: {{ worship.eventPlace.name }}, {{ worship.eventPlace.street }}, {{
|
Adresse: {{ worship.eventPlace.name }}, {{ worship.eventPlace.street }}, {{
|
||||||
|
|||||||
@@ -1,6 +1,168 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="worship-management">
|
<div class="worship-management">
|
||||||
<h2>Gottesdienst Verwaltung</h2>
|
<h2>Gottesdienst Verwaltung</h2>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button type="button" @click="toggleImportSection" class="import-button">
|
||||||
|
{{ showImportSection ? 'Import ausblenden' : 'Import' }}
|
||||||
|
</button>
|
||||||
|
<button type="button" @click="toggleExportSection" class="export-button">
|
||||||
|
{{ showExportSection ? 'Export ausblenden' : 'Export' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="showImportSection" class="import-section">
|
||||||
|
<h3>Gottesdienste importieren</h3>
|
||||||
|
<div class="import-content">
|
||||||
|
<label for="import-file">Datei auswählen (.doc, .docx):</label>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="import-file"
|
||||||
|
ref="fileInput"
|
||||||
|
@change="handleFileSelect"
|
||||||
|
accept=".doc,.docx"
|
||||||
|
/>
|
||||||
|
<div v-if="selectedFile" class="selected-file">
|
||||||
|
Ausgewählte Datei: {{ selectedFile.name }}
|
||||||
|
</div>
|
||||||
|
<button type="button" @click="importWorships" :disabled="!selectedFile || isImporting" class="submit-import-button">
|
||||||
|
{{ isImporting ? 'Importiere...' : 'Importieren' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="showExportSection" class="export-section">
|
||||||
|
<h3>Gottesdienste exportieren</h3>
|
||||||
|
<div class="export-content">
|
||||||
|
<label for="export-date-from">Von Datum:</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
id="export-date-from"
|
||||||
|
v-model="exportDateFrom"
|
||||||
|
/>
|
||||||
|
<label for="export-date-to">Bis Datum:</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
id="export-date-to"
|
||||||
|
v-model="exportDateTo"
|
||||||
|
/>
|
||||||
|
<label for="export-format">Export-Format:</label>
|
||||||
|
<select id="export-format" v-model="exportFormat">
|
||||||
|
<option value="editing">Für Bearbeitung</option>
|
||||||
|
<option value="newsletter">Für Gemeindebrief</option>
|
||||||
|
</select>
|
||||||
|
<button type="button" @click="exportWorships" :disabled="!exportDateFrom || !exportDateTo || isExporting" class="submit-export-button">
|
||||||
|
{{ isExporting ? 'Exportiere...' : 'Exportieren' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dialog zur Bearbeitung der importierten Gottesdienste -->
|
||||||
|
<div v-if="showImportDialog" class="import-dialog-overlay" @click.self="closeImportDialog">
|
||||||
|
<div class="import-dialog-content">
|
||||||
|
<div class="import-dialog-header">
|
||||||
|
<h3>Importierte Gottesdienste bearbeiten</h3>
|
||||||
|
<button class="close-button" @click="closeImportDialog">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="import-dialog-body">
|
||||||
|
<div v-if="importErrors && importErrors.length > 0" class="import-errors">
|
||||||
|
<h4>Fehler beim Parsen:</h4>
|
||||||
|
<ul>
|
||||||
|
<li v-for="(error, index) in importErrors" :key="index">{{ error }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="imported-worships-list">
|
||||||
|
<div v-for="(worship, index) in importedWorships" :key="index" class="imported-worship-item">
|
||||||
|
<h4>
|
||||||
|
Gottesdienst {{ index + 1 }}
|
||||||
|
<span v-if="worship._isNew" class="new-badge">NEU</span>
|
||||||
|
<span v-else-if="worship._isUpdate" class="update-badge">ÄNDERUNG</span>
|
||||||
|
</h4>
|
||||||
|
<div class="worship-edit-fields">
|
||||||
|
<div class="field-group" :class="{ 'field-changed': isFieldChanged(worship, 'date') }">
|
||||||
|
<label>
|
||||||
|
Datum:
|
||||||
|
<span v-if="isFieldChanged(worship, 'date')" class="old-value">(alt: {{ getOldValue(worship, 'date') }})</span>
|
||||||
|
</label>
|
||||||
|
<input type="date" v-model="worship.date" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label>Tag-Name:</label>
|
||||||
|
<input type="text" v-model="worship.dayName" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group" :class="{ 'field-changed': isFieldChanged(worship, 'time') }">
|
||||||
|
<label>
|
||||||
|
Uhrzeit:
|
||||||
|
<span v-if="isFieldChanged(worship, 'time')" class="old-value">(alt: {{ getOldValue(worship, 'time') }})</span>
|
||||||
|
</label>
|
||||||
|
<input type="time" v-model="worship.time" step="60" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group" :class="{ 'field-changed': isFieldChanged(worship, 'eventPlaceId') }">
|
||||||
|
<label>
|
||||||
|
Ort:
|
||||||
|
<span v-if="isFieldChanged(worship, 'eventPlaceId')" class="old-value">(alt: {{ getOldValue(worship, 'eventPlaceName') || getOldValue(worship, 'eventPlaceId') }})</span>
|
||||||
|
</label>
|
||||||
|
<multiselect
|
||||||
|
v-model="worship.eventPlace"
|
||||||
|
:options="eventPlaces"
|
||||||
|
label="name"
|
||||||
|
track-by="id"
|
||||||
|
placeholder="Veranstaltungsort wählen"
|
||||||
|
@update:modelValue="(value) => { if (value) worship.eventPlaceId = value.id; }"
|
||||||
|
></multiselect>
|
||||||
|
</div>
|
||||||
|
<div class="field-group" :class="{ 'field-changed': isFieldChanged(worship, 'title') }">
|
||||||
|
<label>
|
||||||
|
Titel:
|
||||||
|
<span v-if="isFieldChanged(worship, 'title')" class="old-value">(alt: {{ getOldValue(worship, 'title') }})</span>
|
||||||
|
</label>
|
||||||
|
<input type="text" v-model="worship.title" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group" :class="{ 'field-changed': isFieldChanged(worship, 'organizer') }">
|
||||||
|
<label>
|
||||||
|
Gestalter:
|
||||||
|
<span v-if="isFieldChanged(worship, 'organizer')" class="old-value">(alt: {{ getOldValue(worship, 'organizer') }})</span>
|
||||||
|
</label>
|
||||||
|
<input type="text" v-model="worship.organizer" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group" :class="{ 'field-changed': isFieldChanged(worship, 'collection') }">
|
||||||
|
<label>
|
||||||
|
Kollekte:
|
||||||
|
<span v-if="isFieldChanged(worship, 'collection')" class="old-value">(alt: {{ getOldValue(worship, 'collection') }})</span>
|
||||||
|
</label>
|
||||||
|
<input type="text" v-model="worship.collection" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group" :class="{ 'field-changed': isFieldChanged(worship, 'sacristanService') }">
|
||||||
|
<label>
|
||||||
|
Dienst:
|
||||||
|
<span v-if="isFieldChanged(worship, 'sacristanService')" class="old-value">(alt: {{ getOldValue(worship, 'sacristanService') }})</span>
|
||||||
|
</label>
|
||||||
|
<input type="text" v-model="worship.sacristanService" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group" :class="{ 'field-changed': isFieldChanged(worship, 'organPlaying') }">
|
||||||
|
<label>
|
||||||
|
Orgelspiel:
|
||||||
|
<span v-if="isFieldChanged(worship, 'organPlaying')" class="old-value">(alt: {{ getOldValue(worship, 'organPlaying') }})</span>
|
||||||
|
</label>
|
||||||
|
<input type="text" v-model="worship.organPlaying" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" v-model="worship.approved" />
|
||||||
|
Freigegeben
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button type="button" @click="removeWorship(index)" class="remove-button">Entfernen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="import-dialog-footer">
|
||||||
|
<button type="button" @click="closeImportDialog" class="cancel-button">Abbrechen</button>
|
||||||
|
<button type="button" @click="saveImportedWorships" :disabled="importedWorships.length === 0 || isImporting" class="save-button">
|
||||||
|
{{ isImporting ? 'Speichere...' : 'Speichern' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="liturgical-loader">
|
<div class="liturgical-loader">
|
||||||
<select v-model="selectedYear" class="year-select">
|
<select v-model="selectedYear" class="year-select">
|
||||||
<option v-for="year in availableYears" :key="year" :value="year">{{ year }}</option>
|
<option v-for="year in availableYears" :key="year" :value="year">{{ year }}</option>
|
||||||
@@ -55,6 +217,9 @@
|
|||||||
<label for="neighborInvitation">Einladung zum Nachbarschaftsraum:</label>
|
<label for="neighborInvitation">Einladung zum Nachbarschaftsraum:</label>
|
||||||
<input type="checkbox" id="neighborInvitation" v-model="worshipData.neighborInvitation">
|
<input type="checkbox" id="neighborInvitation" v-model="worshipData.neighborInvitation">
|
||||||
|
|
||||||
|
<label for="approved">Freigegeben:</label>
|
||||||
|
<input type="checkbox" id="approved" v-model="worshipData.approved">
|
||||||
|
|
||||||
<label for="introLine">Einleitungszeile:</label>
|
<label for="introLine">Einleitungszeile:</label>
|
||||||
<input type="text" id="introLine" v-model="worshipData.introLine">
|
<input type="text" id="introLine" v-model="worshipData.introLine">
|
||||||
|
|
||||||
@@ -74,11 +239,27 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="worship in filteredWorships" :key="worship.id"
|
<li
|
||||||
:class="dateIsLowerCurrentDate(worship.date) ? 'old-items' : ''">
|
v-for="worship in filteredWorships"
|
||||||
<span>{{ worship.title }} - {{ formatDate(worship.date) }}, {{ formatTime(worship.time) }}</span>
|
:key="worship.id"
|
||||||
<button @click="editWorship(worship)">Bearbeiten</button>
|
:class="[
|
||||||
<button @click="deleteWorship(worship.id)">Löschen</button>
|
'worship-list-item',
|
||||||
|
{ 'old-items': dateIsLowerCurrentDate(worship.date) },
|
||||||
|
{ 'not-approved': !worship.approved }
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{{ worship.title }} - {{ formatDate(worship.date) }}, {{ formatTime(worship.time) }}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="approve-toggle-button"
|
||||||
|
@click="toggleApproved(worship)"
|
||||||
|
>
|
||||||
|
{{ worship.approved ? 'Freigabe zurücknehmen' : 'Freigeben' }}
|
||||||
|
</button>
|
||||||
|
<button type="button" @click="editWorship(worship)">Bearbeiten</button>
|
||||||
|
<button type="button" @click="deleteWorship(worship.id)">Löschen</button>
|
||||||
<div class="tooltip">{{ getEventPlaceName(worship.eventPlaceId) }}</div>
|
<div class="tooltip">{{ getEventPlaceName(worship.eventPlaceId) }}</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -124,12 +305,24 @@ export default {
|
|||||||
sacristanService: '',
|
sacristanService: '',
|
||||||
website: '',
|
website: '',
|
||||||
dayName: '',
|
dayName: '',
|
||||||
|
approved: false,
|
||||||
},
|
},
|
||||||
selectedEventPlace: null,
|
selectedEventPlace: null,
|
||||||
editMode: false,
|
editMode: false,
|
||||||
editId: null,
|
editId: null,
|
||||||
searchDate: '',
|
searchDate: '',
|
||||||
showPastWorships: false,
|
showPastWorships: false,
|
||||||
|
showImportSection: false,
|
||||||
|
selectedFile: null,
|
||||||
|
isImporting: false,
|
||||||
|
showExportSection: false,
|
||||||
|
exportDateFrom: '',
|
||||||
|
exportDateTo: '',
|
||||||
|
exportFormat: 'editing',
|
||||||
|
isExporting: false,
|
||||||
|
showImportDialog: false,
|
||||||
|
importedWorships: [],
|
||||||
|
importErrors: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -184,6 +377,15 @@ export default {
|
|||||||
await this.fetchLiturgicalDays();
|
await this.fetchLiturgicalDays();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
isFieldChanged(worship, fieldName) {
|
||||||
|
return worship._changedFields && worship._changedFields.includes(fieldName);
|
||||||
|
},
|
||||||
|
getOldValue(worship, fieldName) {
|
||||||
|
if (worship._oldValues && worship._oldValues[fieldName]) {
|
||||||
|
return worship._oldValues[fieldName];
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
formatTime,
|
formatTime,
|
||||||
formatDate,
|
formatDate,
|
||||||
async fetchWorships() {
|
async fetchWorships() {
|
||||||
@@ -323,7 +525,8 @@ export default {
|
|||||||
eventPlaceId: this.selectedEventPlace ? this.selectedEventPlace.id : null,
|
eventPlaceId: this.selectedEventPlace ? this.selectedEventPlace.id : null,
|
||||||
organizer: this.selectedOrganizers.map(org => org.name).join(', '),
|
organizer: this.selectedOrganizers.map(org => org.name).join(', '),
|
||||||
sacristanService: this.selectedSacristans.map(sac => sac.name).join(', '),
|
sacristanService: this.selectedSacristans.map(sac => sac.name).join(', '),
|
||||||
dayName: this.selectedDayName ? this.selectedDayName.dayName : ''
|
dayName: this.selectedDayName ? this.selectedDayName.dayName : '',
|
||||||
|
approved: !!this.worshipData.approved,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.editMode) {
|
if (this.editMode) {
|
||||||
@@ -339,6 +542,16 @@ export default {
|
|||||||
console.error('Fehler beim Speichern des Gottesdienstes:', error);
|
console.error('Fehler beim Speichern des Gottesdienstes:', error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async toggleApproved(worship) {
|
||||||
|
try {
|
||||||
|
const newApproved = !worship.approved;
|
||||||
|
await axios.put(`/worships/${worship.id}`, { approved: newApproved });
|
||||||
|
worship.approved = newApproved;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Aktualisieren des Freigabe-Status:', error);
|
||||||
|
alert('Fehler beim Aktualisieren des Freigabe-Status.');
|
||||||
|
}
|
||||||
|
},
|
||||||
editWorship(worship) {
|
editWorship(worship) {
|
||||||
this.worshipData = { ...worship };
|
this.worshipData = { ...worship };
|
||||||
this.worshipData.date = formatDate(worship.date).split(".").reverse().join("-");
|
this.worshipData.date = formatDate(worship.date).split(".").reverse().join("-");
|
||||||
@@ -388,7 +601,8 @@ export default {
|
|||||||
highlightTime: false,
|
highlightTime: false,
|
||||||
neighborInvitation: false,
|
neighborInvitation: false,
|
||||||
introLine: '',
|
introLine: '',
|
||||||
dayName: ''
|
dayName: '',
|
||||||
|
approved: false,
|
||||||
};
|
};
|
||||||
this.selectedEventPlace = null;
|
this.selectedEventPlace = null;
|
||||||
this.selectedOrganizers = [];
|
this.selectedOrganizers = [];
|
||||||
@@ -429,6 +643,249 @@ export default {
|
|||||||
this.dayNameOptions.push(tag);
|
this.dayNameOptions.push(tag);
|
||||||
this.selectedDayName = tag;
|
this.selectedDayName = tag;
|
||||||
this.worshipData.dayName = newTag;
|
this.worshipData.dayName = newTag;
|
||||||
|
},
|
||||||
|
toggleImportSection() {
|
||||||
|
this.showImportSection = !this.showImportSection;
|
||||||
|
if (!this.showImportSection) {
|
||||||
|
// Reset beim Ausblenden
|
||||||
|
this.selectedFile = null;
|
||||||
|
if (this.$refs.fileInput) {
|
||||||
|
this.$refs.fileInput.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleFileSelect(event) {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
if (file) {
|
||||||
|
// Validierung: Nur .doc und .docx Dateien erlauben
|
||||||
|
const allowedExtensions = ['.doc', '.docx'];
|
||||||
|
const fileName = file.name.toLowerCase();
|
||||||
|
const isValidFile = allowedExtensions.some(ext => fileName.endsWith(ext));
|
||||||
|
|
||||||
|
if (!isValidFile) {
|
||||||
|
alert('Bitte wählen Sie nur .doc oder .docx Dateien aus.');
|
||||||
|
event.target.value = '';
|
||||||
|
this.selectedFile = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedFile = file;
|
||||||
|
} else {
|
||||||
|
this.selectedFile = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async importWorships() {
|
||||||
|
if (!this.selectedFile) {
|
||||||
|
alert('Bitte wählen Sie eine Datei aus.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isImporting = true;
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', this.selectedFile);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/worships/import', formData, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Geparste Daten im Dialog anzeigen
|
||||||
|
if (response.data.worships && response.data.worships.length > 0) {
|
||||||
|
// EventPlace-Objekte zuordnen
|
||||||
|
// Das Datum kommt bereits im YYYY-MM-DD Format vom Backend
|
||||||
|
this.importedWorships = response.data.worships.map(w => {
|
||||||
|
const eventPlace = this.eventPlaces.find(ep => ep.id === w.eventPlaceId);
|
||||||
|
// Normalisiere Uhrzeit: entferne Sekunden für Anzeige
|
||||||
|
let timeValue = w.time;
|
||||||
|
if (timeValue && typeof timeValue === 'string' && timeValue.length > 5) {
|
||||||
|
timeValue = timeValue.substring(0, 5);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...w,
|
||||||
|
// Stelle sicher, dass das Datum ein String im YYYY-MM-DD Format ist
|
||||||
|
date: typeof w.date === 'string' ? w.date.split('T')[0] : w.date,
|
||||||
|
time: timeValue,
|
||||||
|
eventPlace: eventPlace || null,
|
||||||
|
approved: false,
|
||||||
|
// Stelle sicher, dass _changedFields und _oldValues erhalten bleiben
|
||||||
|
_changedFields: w._changedFields || [],
|
||||||
|
_oldValues: w._oldValues || {},
|
||||||
|
_isUpdate: w._isUpdate || false,
|
||||||
|
_isNew: w._isNew || false,
|
||||||
|
_existingId: w._existingId || null
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.importErrors = response.data.errors || [];
|
||||||
|
this.showImportDialog = true;
|
||||||
|
} else {
|
||||||
|
alert('Keine Gottesdienste in der Datei gefunden.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Importieren der Gottesdienste:', error);
|
||||||
|
const errorMessage = error.response?.data?.message || 'Fehler beim Importieren der Datei.';
|
||||||
|
alert('Fehler: ' + errorMessage);
|
||||||
|
} finally {
|
||||||
|
this.isImporting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
closeImportDialog() {
|
||||||
|
this.showImportDialog = false;
|
||||||
|
this.importedWorships = [];
|
||||||
|
this.importErrors = [];
|
||||||
|
this.selectedFile = null;
|
||||||
|
if (this.$refs.fileInput) {
|
||||||
|
this.$refs.fileInput.value = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeWorship(index) {
|
||||||
|
this.importedWorships.splice(index, 1);
|
||||||
|
},
|
||||||
|
async saveImportedWorships() {
|
||||||
|
if (this.importedWorships.length === 0) {
|
||||||
|
alert('Keine Gottesdienste zum Speichern vorhanden.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isImporting = true;
|
||||||
|
|
||||||
|
// Daten für das Backend vorbereiten
|
||||||
|
const worshipsToSave = this.importedWorships.map(w => {
|
||||||
|
// Stelle sicher, dass das Datum im richtigen Format ist (YYYY-MM-DD)
|
||||||
|
let dateStr = w.date;
|
||||||
|
if (w.date instanceof Date) {
|
||||||
|
const year = w.date.getFullYear();
|
||||||
|
const month = String(w.date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(w.date.getDate()).padStart(2, '0');
|
||||||
|
dateStr = `${year}-${month}-${day}`;
|
||||||
|
} else if (typeof w.date === 'string') {
|
||||||
|
// Falls bereits String, verwende direkt (sollte YYYY-MM-DD sein)
|
||||||
|
dateStr = w.date.split('T')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stelle sicher, dass die Uhrzeit im Format HH:MM:00 ist (mit Sekunden für DB)
|
||||||
|
let timeValue = w.time;
|
||||||
|
if (timeValue && typeof timeValue === 'string') {
|
||||||
|
// Wenn nur HH:MM vorhanden, füge :00 hinzu
|
||||||
|
if (timeValue.length === 5) {
|
||||||
|
timeValue = timeValue + ':00';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const worshipData = {
|
||||||
|
date: dateStr,
|
||||||
|
dayName: w.dayName,
|
||||||
|
time: timeValue,
|
||||||
|
title: w.title,
|
||||||
|
organizer: w.organizer,
|
||||||
|
collection: w.collection,
|
||||||
|
sacristanService: w.sacristanService,
|
||||||
|
organPlaying: w.organPlaying,
|
||||||
|
approved: w.approved || false,
|
||||||
|
eventPlaceId: w.eventPlace ? w.eventPlace.id : (w.eventPlaceId || null),
|
||||||
|
};
|
||||||
|
return worshipData;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/worships/import/save', {
|
||||||
|
worships: worshipsToSave
|
||||||
|
});
|
||||||
|
|
||||||
|
// Erfolgsmeldung anzeigen
|
||||||
|
let message = response.data.message || 'Import erfolgreich abgeschlossen!';
|
||||||
|
if (response.data.imported !== undefined || response.data.updated !== undefined) {
|
||||||
|
message = `Import abgeschlossen!\n`;
|
||||||
|
if (response.data.imported !== undefined) {
|
||||||
|
message += `- ${response.data.imported} neue Gottesdienste erstellt\n`;
|
||||||
|
}
|
||||||
|
if (response.data.updated !== undefined) {
|
||||||
|
message += `- ${response.data.updated} Gottesdienste aktualisiert\n`;
|
||||||
|
}
|
||||||
|
if (response.data.skipped !== undefined && response.data.skipped > 0) {
|
||||||
|
message += `- ${response.data.skipped} übersprungen (vergangene Daten)\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (response.data.errors && response.data.errors.length > 0) {
|
||||||
|
message += `\nFehler: ${response.data.errors.length}`;
|
||||||
|
}
|
||||||
|
alert(message);
|
||||||
|
|
||||||
|
// Dialog schließen und Daten aktualisieren
|
||||||
|
this.closeImportDialog();
|
||||||
|
await this.fetchWorships();
|
||||||
|
await this.fetchWorshipOptions();
|
||||||
|
await this.fetchLiturgicalDays();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Speichern der Gottesdienste:', error);
|
||||||
|
const errorMessage = error.response?.data?.message || 'Fehler beim Speichern der Gottesdienste.';
|
||||||
|
alert('Fehler: ' + errorMessage);
|
||||||
|
} finally {
|
||||||
|
this.isImporting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleExportSection() {
|
||||||
|
this.showExportSection = !this.showExportSection;
|
||||||
|
if (!this.showExportSection) {
|
||||||
|
// Reset beim Ausblenden
|
||||||
|
this.exportDateFrom = '';
|
||||||
|
this.exportDateTo = '';
|
||||||
|
this.exportFormat = 'editing';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async exportWorships() {
|
||||||
|
if (!this.exportDateFrom || !this.exportDateTo) {
|
||||||
|
alert('Bitte wählen Sie einen Datumsbereich aus.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new Date(this.exportDateFrom) > new Date(this.exportDateTo)) {
|
||||||
|
alert('Das "Von Datum" muss vor dem "Bis Datum" liegen.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isExporting = true;
|
||||||
|
try {
|
||||||
|
const response = await axios.get('/worships/export', {
|
||||||
|
params: {
|
||||||
|
from: this.exportDateFrom,
|
||||||
|
to: this.exportDateTo,
|
||||||
|
format: this.exportFormat
|
||||||
|
},
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Datei herunterladen
|
||||||
|
const blob = new Blob([response.data], {
|
||||||
|
type: response.headers['content-type'] || 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||||
|
});
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
|
||||||
|
// Dateiname aus Content-Disposition Header extrahieren oder Standardnamen verwenden
|
||||||
|
const contentDisposition = response.headers['content-disposition'];
|
||||||
|
let filename = `gottesdienste_${this.exportDateFrom}_${this.exportDateTo}.docx`;
|
||||||
|
if (contentDisposition) {
|
||||||
|
const filenameMatch = contentDisposition.match(/filename="?(.+)"?/i);
|
||||||
|
if (filenameMatch) {
|
||||||
|
filename = filenameMatch[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
link.download = filename;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Exportieren der Gottesdienste:', error);
|
||||||
|
const errorMessage = error.response?.data?.message || 'Fehler beim Exportieren der Datei.';
|
||||||
|
alert('Fehler: ' + errorMessage);
|
||||||
|
} finally {
|
||||||
|
this.isExporting = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -599,6 +1056,10 @@ li {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.worship-list-item {
|
||||||
|
transition: background-color 0.2s ease, border-left-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
@@ -633,4 +1094,436 @@ li>span {
|
|||||||
.old-items {
|
.old-items {
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.not-approved {
|
||||||
|
background-color: #fff8e1; /* zartes Gelb für noch nicht freigegebene Gottesdienste */
|
||||||
|
border-left: 4px solid #ffb300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.approve-toggle-button {
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #4CAF50;
|
||||||
|
background-color: #e8f5e9;
|
||||||
|
color: #2e7d32;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.approve-toggle-button:hover {
|
||||||
|
background-color: #c8e6c9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin: 0;
|
||||||
|
height: 36px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-button:hover {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-section {
|
||||||
|
border: 2px solid #ddd;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-section h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #333;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-content label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #555;
|
||||||
|
margin: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-content input[type="file"] {
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-content input[type="file"]:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-file {
|
||||||
|
padding: 8px;
|
||||||
|
background-color: #e8f5e9;
|
||||||
|
border: 1px solid #4CAF50;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #2e7d32;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-import-button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: #2196F3;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
align-self: flex-start;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-import-button:hover:not(:disabled) {
|
||||||
|
background-color: #1976D2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-import-button:disabled {
|
||||||
|
background-color: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: #FF9800;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin: 0;
|
||||||
|
height: 36px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-button:hover {
|
||||||
|
background-color: #F57C00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-section {
|
||||||
|
border: 2px solid #ddd;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-section h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #333;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-content label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #555;
|
||||||
|
margin: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-content input[type="date"],
|
||||||
|
.export-content select {
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-content input[type="date"]:focus,
|
||||||
|
.export-content select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #FF9800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-export-button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: #FF9800;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
align-self: flex-start;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-export-button:hover:not(:disabled) {
|
||||||
|
background-color: #F57C00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-export-button:disabled {
|
||||||
|
background-color: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-dialog-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-dialog-content {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
max-width: 90%;
|
||||||
|
max-height: 90vh;
|
||||||
|
width: 1200px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-dialog-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-dialog-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 28px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #aaa;
|
||||||
|
padding: 0;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-dialog-body {
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-errors {
|
||||||
|
background-color: #ffebee;
|
||||||
|
border: 1px solid #f44336;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-errors h4 {
|
||||||
|
margin-top: 0;
|
||||||
|
color: #c62828;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-errors ul {
|
||||||
|
margin: 10px 0 0 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-errors li {
|
||||||
|
color: #c62828;
|
||||||
|
padding: 5px 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imported-worships-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imported-worship-item {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imported-worship-item h4 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #333;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-badge {
|
||||||
|
background-color: #4caf50;
|
||||||
|
color: white;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-badge {
|
||||||
|
background-color: #ff9800;
|
||||||
|
color: white;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.worship-edit-fields {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group label {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group input[type="text"],
|
||||||
|
.field-group input[type="date"],
|
||||||
|
.field-group input[type="time"] {
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group.field-changed {
|
||||||
|
background-color: #fff3cd;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ffc107;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group.field-changed label {
|
||||||
|
color: #856404;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group .old-value {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #856404;
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: italic;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group input[type="checkbox"] {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-group .multiselect {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-button {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: #f44336;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-button:hover {
|
||||||
|
background-color: #d32f2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-dialog-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-button,
|
||||||
|
.save-button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-button {
|
||||||
|
background-color: #ccc;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-button:hover {
|
||||||
|
background-color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-button {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-button:hover:not(:disabled) {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-button:disabled {
|
||||||
|
background-color: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import axios from './axios';
|
|||||||
import './assets/css/editor.css';
|
import './assets/css/editor.css';
|
||||||
|
|
||||||
async function fetchMenuData() {
|
async function fetchMenuData() {
|
||||||
const response = await fetch(process.env.VUE_APP_BACKEND_URL + '/menu-data');
|
const backendUrl = process.env.VUE_APP_BACKEND_URL || '';
|
||||||
|
const response = await fetch(backendUrl + '/menu-data');
|
||||||
return await response.json();
|
return await response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,8 +65,12 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
|
|
||||||
next({ ...to, replace: true });
|
next({ ...to, replace: true });
|
||||||
} else {
|
} else {
|
||||||
|
// Sicherstellen, dass die Login-Route immer verfügbar ist
|
||||||
|
if (!router.hasRoute('auth-login')) {
|
||||||
|
addAuthLoginRoute();
|
||||||
|
}
|
||||||
if (to.matched.some(record => record.meta.requiresAuth) && !store.getters.isLoggedIn) {
|
if (to.matched.some(record => record.meta.requiresAuth) && !store.getters.isLoggedIn) {
|
||||||
next('/login');
|
next('/auth/login');
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ export default createStore({
|
|||||||
localStorage.removeItem('isLoggedIn');
|
localStorage.removeItem('isLoggedIn');
|
||||||
localStorage.removeItem('user');
|
localStorage.removeItem('user');
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('token');
|
||||||
router.push('/auth/login');
|
|
||||||
},
|
},
|
||||||
setMenuData(state, menuData) {
|
setMenuData(state, menuData) {
|
||||||
state.menuData = menuData;
|
state.menuData = menuData;
|
||||||
@@ -101,6 +100,10 @@ export default createStore({
|
|||||||
console.error('Fehler beim Logout:', error);
|
console.error('Fehler beim Logout:', error);
|
||||||
} finally {
|
} finally {
|
||||||
commit('logout');
|
commit('logout');
|
||||||
|
// Navigation nach Logout mit replace, damit die Login-Seite direkt erreichbar ist
|
||||||
|
if (router.currentRoute.value.path !== '/auth/login') {
|
||||||
|
router.replace('/auth/login');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,24 @@ module.exports = defineConfig({
|
|||||||
transpileDependencies: [],
|
transpileDependencies: [],
|
||||||
devServer: {
|
devServer: {
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
port: 8080
|
// Port kann über VUE_APP_FRONTEND_PORT oder FRONTEND_PORT in .env gesetzt werden
|
||||||
|
port: parseInt(process.env.VUE_APP_FRONTEND_PORT || process.env.FRONTEND_PORT || '8080', 10),
|
||||||
|
// Proxy für API-Requests zum Backend-Server
|
||||||
|
// Backend sollte auf einem anderen Port laufen (z.B. 3010)
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: process.env.VUE_APP_BACKEND_PROXY || 'http://torstens:3010',
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false,
|
||||||
|
logLevel: 'debug',
|
||||||
|
// Erhöhe Header-Limits für Proxy
|
||||||
|
headers: {
|
||||||
|
'Connection': 'keep-alive'
|
||||||
|
},
|
||||||
|
// Erhöhe Timeout für große Requests
|
||||||
|
timeout: 60000
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
configureWebpack: {
|
configureWebpack: {
|
||||||
output: { clean: true },
|
output: { clean: true },
|
||||||
|
|||||||
Reference in New Issue
Block a user