feat(db): init mongo and passing komga conf
This commit is contained in:
7
.env
Normal file
7
.env
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# MongoDB
|
||||||
|
MONGO_USER=admin
|
||||||
|
MONGO_PASSWORD=password
|
||||||
|
MONGODB_URI=mongodb://admin:password@localhost:27017/paniels?authSource=admin
|
||||||
|
|
||||||
|
# Komga
|
||||||
|
NEXT_PUBLIC_API_URL=https://cloud.julienfroidefond.com
|
||||||
7
.env.example
Normal file
7
.env.example
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# MongoDB
|
||||||
|
MONGO_USER=admin
|
||||||
|
MONGO_PASSWORD=password
|
||||||
|
MONGODB_URI=mongodb://admin:password@localhost:27017/paniels?authSource=admin
|
||||||
|
|
||||||
|
# Komga
|
||||||
|
NEXT_PUBLIC_API_URL=https://cloud.julienfroidefond.com
|
||||||
@@ -16,3 +16,31 @@ services:
|
|||||||
- NODE_ENV=development
|
- NODE_ENV=development
|
||||||
- NEXT_PUBLIC_API_URL=https://cloud.julienfroidefond.com
|
- NEXT_PUBLIC_API_URL=https://cloud.julienfroidefond.com
|
||||||
command: npm run dev
|
command: npm run dev
|
||||||
|
|
||||||
|
mongodb:
|
||||||
|
image: mongo:latest
|
||||||
|
container_name: paniels_mongodb
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER}
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD}
|
||||||
|
ports:
|
||||||
|
- "27017:27017"
|
||||||
|
volumes:
|
||||||
|
- mongodb_data:/data/db
|
||||||
|
|
||||||
|
mongo-express:
|
||||||
|
image: mongo-express:latest
|
||||||
|
container_name: paniels_mongo_express
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "8081:8081"
|
||||||
|
environment:
|
||||||
|
ME_CONFIG_MONGODB_ADMINUSERNAME: ${MONGO_USER}
|
||||||
|
ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGO_PASSWORD}
|
||||||
|
ME_CONFIG_MONGODB_URL: mongodb://${MONGO_USER}:${MONGO_PASSWORD}@mongodb:27017/
|
||||||
|
depends_on:
|
||||||
|
- mongodb
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mongodb_data:
|
||||||
|
|||||||
360
package-lock.json
generated
360
package-lock.json
generated
@@ -12,15 +12,17 @@
|
|||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
"@radix-ui/react-toast": "^1.2.6",
|
"@radix-ui/react-toast": "^1.2.6",
|
||||||
|
"@types/mongoose": "^5.11.97",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.323.0",
|
"lucide-react": "^0.323.0",
|
||||||
|
"mongoose": "^8.10.0",
|
||||||
"next": "^14.1.0",
|
"next": "^14.1.0",
|
||||||
|
"next-auth": "^4.24.11",
|
||||||
"next-themes": "^0.4.4",
|
"next-themes": "^0.4.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"sharp": "^0.33.2",
|
"sharp": "^0.33.2",
|
||||||
"sonner": "^1.7.4",
|
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
@@ -52,6 +54,17 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.26.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz",
|
||||||
|
"integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==",
|
||||||
|
"dependencies": {
|
||||||
|
"regenerator-runtime": "^0.14.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@emnapi/runtime": {
|
"node_modules/@emnapi/runtime": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
|
||||||
@@ -654,6 +667,14 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mongodb-js/saslprep": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-+ywrb0AqkfaYuhHs6LxKWgqbh3I72EpEgESCw37o+9qPx9WTCkgDm2B+eMrwehGtHBWHFU4GXvnSCNiFhhausg==",
|
||||||
|
"dependencies": {
|
||||||
|
"sparse-bitfield": "^3.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@next/env": {
|
"node_modules/@next/env": {
|
||||||
"version": "14.2.23",
|
"version": "14.2.23",
|
||||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.23.tgz",
|
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.23.tgz",
|
||||||
@@ -859,6 +880,14 @@
|
|||||||
"node": ">=12.4.0"
|
"node": ">=12.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@panva/hkdf": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/panva"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pkgjs/parseargs": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
@@ -1516,6 +1545,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/mongoose": {
|
||||||
|
"version": "5.11.97",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.11.97.tgz",
|
||||||
|
"integrity": "sha512-cqwOVYT3qXyLiGw7ueU2kX9noE8DPGRY6z8eUxudhXY8NZ7DMKYAxyZkLSevGfhCX3dO/AoX5/SO9lAzfjon0Q==",
|
||||||
|
"deprecated": "Mongoose publishes its own types, so you do not need to install this package.",
|
||||||
|
"dependencies": {
|
||||||
|
"mongoose": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.17.17",
|
"version": "20.17.17",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.17.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.17.tgz",
|
||||||
@@ -1560,6 +1598,19 @@
|
|||||||
"integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
|
"integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/webidl-conversions": {
|
||||||
|
"version": "7.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
|
||||||
|
"integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/whatwg-url": {
|
||||||
|
"version": "11.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
|
||||||
|
"integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/webidl-conversions": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
|
||||||
@@ -2237,6 +2288,14 @@
|
|||||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bson": {
|
||||||
|
"version": "6.10.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bson/-/bson-6.10.2.tgz",
|
||||||
|
"integrity": "sha512-5afhLTjqDSA3akH56E+/2J6kTDuSIlBxyXPdQslj9hcIgOUE378xdOfZvC/9q3LifJNI6KR/juZ+d0NRNYBwXg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/busboy": {
|
"node_modules/busboy": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||||
@@ -2472,6 +2531,14 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/cookie": {
|
||||||
|
"version": "0.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||||
|
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
@@ -2570,7 +2637,6 @@
|
|||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "^2.1.3"
|
"ms": "^2.1.3"
|
||||||
@@ -4471,6 +4537,14 @@
|
|||||||
"jiti": "bin/jiti.js"
|
"jiti": "bin/jiti.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jose": {
|
||||||
|
"version": "4.15.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
|
||||||
|
"integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/panva"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
@@ -4540,6 +4614,14 @@
|
|||||||
"node": ">=4.0"
|
"node": ">=4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/kareem": {
|
||||||
|
"version": "2.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
|
||||||
|
"integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/keyv": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||||
@@ -4662,6 +4744,11 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/memory-pager": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
|
||||||
|
},
|
||||||
"node_modules/merge2": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
@@ -4716,11 +4803,104 @@
|
|||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mongodb": {
|
||||||
|
"version": "6.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.13.0.tgz",
|
||||||
|
"integrity": "sha512-KeESYR5TEaFxOuwRqkOm3XOsMqCSkdeDMjaW5u2nuKfX7rqaofp7JQGoi7sVqQcNJTKuveNbzZtWMstb8ABP6Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@mongodb-js/saslprep": "^1.1.9",
|
||||||
|
"bson": "^6.10.1",
|
||||||
|
"mongodb-connection-string-url": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@aws-sdk/credential-providers": "^3.188.0",
|
||||||
|
"@mongodb-js/zstd": "^1.1.0 || ^2.0.0",
|
||||||
|
"gcp-metadata": "^5.2.0",
|
||||||
|
"kerberos": "^2.0.1",
|
||||||
|
"mongodb-client-encryption": ">=6.0.0 <7",
|
||||||
|
"snappy": "^7.2.2",
|
||||||
|
"socks": "^2.7.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@aws-sdk/credential-providers": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@mongodb-js/zstd": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"gcp-metadata": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"kerberos": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"mongodb-client-encryption": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"snappy": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"socks": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/whatwg-url": "^11.0.2",
|
||||||
|
"whatwg-url": "^14.1.0 || ^13.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongoose": {
|
||||||
|
"version": "8.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.10.0.tgz",
|
||||||
|
"integrity": "sha512-nLhk3Qrv6q/HpD2k1O7kbBqsq+/kmKpdv5KJ+LLhQlII3e1p/SSLoLP6jMuSiU6+iLK7zFw4T1niAk3mA3QVug==",
|
||||||
|
"dependencies": {
|
||||||
|
"bson": "^6.10.1",
|
||||||
|
"kareem": "2.6.3",
|
||||||
|
"mongodb": "~6.13.0",
|
||||||
|
"mpath": "0.9.0",
|
||||||
|
"mquery": "5.0.0",
|
||||||
|
"ms": "2.1.3",
|
||||||
|
"sift": "17.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.20.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mongoose"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mpath": {
|
||||||
|
"version": "0.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
|
||||||
|
"integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mquery": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "4.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/mz": {
|
"node_modules/mz": {
|
||||||
@@ -4809,6 +4989,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/next-auth": {
|
||||||
|
"version": "4.24.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.11.tgz",
|
||||||
|
"integrity": "sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.20.13",
|
||||||
|
"@panva/hkdf": "^1.0.2",
|
||||||
|
"cookie": "^0.7.0",
|
||||||
|
"jose": "^4.15.5",
|
||||||
|
"oauth": "^0.9.15",
|
||||||
|
"openid-client": "^5.4.0",
|
||||||
|
"preact": "^10.6.3",
|
||||||
|
"preact-render-to-string": "^5.1.19",
|
||||||
|
"uuid": "^8.3.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@auth/core": "0.34.2",
|
||||||
|
"next": "^12.2.5 || ^13 || ^14 || ^15",
|
||||||
|
"nodemailer": "^6.6.5",
|
||||||
|
"react": "^17.0.2 || ^18 || ^19",
|
||||||
|
"react-dom": "^17.0.2 || ^18 || ^19"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@auth/core": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"nodemailer": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/next-themes": {
|
"node_modules/next-themes": {
|
||||||
"version": "0.4.4",
|
"version": "0.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.4.tgz",
|
||||||
@@ -4872,6 +5083,11 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/oauth": {
|
||||||
|
"version": "0.9.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
|
||||||
|
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@@ -5002,6 +5218,14 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/oidc-token-hash": {
|
||||||
|
"version": "5.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz",
|
||||||
|
"integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==",
|
||||||
|
"engines": {
|
||||||
|
"node": "^10.13.0 || >=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/once": {
|
"node_modules/once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
@@ -5012,6 +5236,39 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/openid-client": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==",
|
||||||
|
"dependencies": {
|
||||||
|
"jose": "^4.15.9",
|
||||||
|
"lru-cache": "^6.0.0",
|
||||||
|
"object-hash": "^2.2.0",
|
||||||
|
"oidc-token-hash": "^5.0.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/panva"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/openid-client/node_modules/lru-cache": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/openid-client/node_modules/object-hash": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"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",
|
||||||
@@ -5343,6 +5600,26 @@
|
|||||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/preact": {
|
||||||
|
"version": "10.25.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/preact/-/preact-10.25.4.tgz",
|
||||||
|
"integrity": "sha512-jLdZDb+Q+odkHJ+MpW/9U5cODzqnB+fy2EiHSZES7ldV5LK7yjlVzTp7R8Xy6W6y75kfK8iWYtFVH7lvjwrCMA==",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/preact"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/preact-render-to-string": {
|
||||||
|
"version": "5.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz",
|
||||||
|
"integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==",
|
||||||
|
"dependencies": {
|
||||||
|
"pretty-format": "^3.8.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"preact": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prelude-ls": {
|
"node_modules/prelude-ls": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||||
@@ -5353,6 +5630,11 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pretty-format": {
|
||||||
|
"version": "3.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
|
||||||
|
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
|
||||||
|
},
|
||||||
"node_modules/prop-types": {
|
"node_modules/prop-types": {
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
@@ -5369,7 +5651,6 @@
|
|||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
@@ -5540,6 +5821,11 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/regenerator-runtime": {
|
||||||
|
"version": "0.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||||
|
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||||
|
},
|
||||||
"node_modules/regexp.prototype.flags": {
|
"node_modules/regexp.prototype.flags": {
|
||||||
"version": "1.5.4",
|
"version": "1.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
|
||||||
@@ -5934,6 +6220,11 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sift": {
|
||||||
|
"version": "17.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz",
|
||||||
|
"integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ=="
|
||||||
|
},
|
||||||
"node_modules/signal-exit": {
|
"node_modules/signal-exit": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||||
@@ -5965,15 +6256,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sonner": {
|
|
||||||
"version": "1.7.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.4.tgz",
|
|
||||||
"integrity": "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
|
||||||
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
@@ -5983,6 +6265,14 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sparse-bitfield": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"memory-pager": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/stable-hash": {
|
"node_modules/stable-hash": {
|
||||||
"version": "0.0.4",
|
"version": "0.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz",
|
||||||
@@ -6399,6 +6689,17 @@
|
|||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ts-api-utils": {
|
"node_modules/ts-api-utils": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz",
|
||||||
@@ -6671,6 +6972,34 @@
|
|||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/uuid": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "14.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.1.tgz",
|
||||||
|
"integrity": "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "^5.0.0",
|
||||||
|
"webidl-conversions": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
@@ -6885,6 +7214,11 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/yallist": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
|
},
|
||||||
"node_modules/yaml": {
|
"node_modules/yaml": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz",
|
||||||
|
|||||||
@@ -14,10 +14,13 @@
|
|||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
"@radix-ui/react-toast": "^1.2.6",
|
"@radix-ui/react-toast": "^1.2.6",
|
||||||
|
"@types/mongoose": "^5.11.97",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.323.0",
|
"lucide-react": "^0.323.0",
|
||||||
|
"mongoose": "^8.10.0",
|
||||||
"next": "^14.1.0",
|
"next": "^14.1.0",
|
||||||
|
"next-auth": "^4.24.11",
|
||||||
"next-themes": "^0.4.4",
|
"next-themes": "^0.4.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|||||||
@@ -1,16 +1,57 @@
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
import { ConfigDBService } from "@/lib/services/config-db.service";
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
try {
|
try {
|
||||||
const data = await request.json();
|
const data = await request.json();
|
||||||
console.log("Configuration Komga reçue:", data);
|
const mongoConfig = await ConfigDBService.saveConfig(data);
|
||||||
|
// Convertir le document Mongoose en objet simple
|
||||||
return NextResponse.json({ message: "Configuration reçue avec succès" }, { status: 200 });
|
const config = {
|
||||||
} catch (error) {
|
url: mongoConfig.url,
|
||||||
console.error("Erreur lors de la réception de la configuration:", error);
|
username: mongoConfig.username,
|
||||||
|
password: mongoConfig.password,
|
||||||
|
userId: mongoConfig.userId,
|
||||||
|
};
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: "Erreur lors de la réception de la configuration" },
|
{ message: "Configuration sauvegardée avec succès", config },
|
||||||
{ status: 400 }
|
{ status: 200 }
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur lors de la sauvegarde de la configuration:", error);
|
||||||
|
if (error instanceof Error && error.message === "Utilisateur non authentifié") {
|
||||||
|
return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
|
||||||
|
}
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Erreur lors de la sauvegarde de la configuration" },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
try {
|
||||||
|
const mongoConfig = await ConfigDBService.getConfig();
|
||||||
|
// Convertir le document Mongoose en objet simple
|
||||||
|
const config = {
|
||||||
|
url: mongoConfig.url,
|
||||||
|
username: mongoConfig.username,
|
||||||
|
password: mongoConfig.password,
|
||||||
|
userId: mongoConfig.userId,
|
||||||
|
};
|
||||||
|
return NextResponse.json(config, { status: 200 });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur lors de la récupération de la configuration:", error);
|
||||||
|
if (error instanceof Error) {
|
||||||
|
if (error.message === "Utilisateur non authentifié") {
|
||||||
|
return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
|
||||||
|
}
|
||||||
|
if (error.message === "Configuration non trouvée") {
|
||||||
|
return NextResponse.json({ error: "Configuration non trouvée" }, { status: 404 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Erreur lors de la récupération de la configuration" },
|
||||||
|
{ status: 500 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,429 +1,24 @@
|
|||||||
"use client";
|
import { ConfigDBService } from "@/lib/services/config-db.service";
|
||||||
|
import { ClientSettings } from "@/components/settings/ClientSettings";
|
||||||
|
|
||||||
import { useState, useEffect, useRef } from "react";
|
export default async function SettingsPage() {
|
||||||
import { Loader2, Network, Trash2 } from "lucide-react";
|
let config = null;
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { storageService } from "@/lib/services/storage.service";
|
|
||||||
import { AuthError } from "@/types/auth";
|
|
||||||
import { useToast } from "@/components/ui/use-toast";
|
|
||||||
import { komgaConfigService } from "@/lib/services/komga-config.service";
|
|
||||||
|
|
||||||
interface ErrorMessage {
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function SettingsPage() {
|
|
||||||
const router = useRouter();
|
|
||||||
const { toast } = useToast();
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
const [isCacheClearing, setIsCacheClearing] = useState(false);
|
|
||||||
const [error, setError] = useState<AuthError | null>(null);
|
|
||||||
const [success, setSuccess] = useState<string | null>(null);
|
|
||||||
const [config, setConfig] = useState({
|
|
||||||
serverUrl: "",
|
|
||||||
username: "",
|
|
||||||
password: "",
|
|
||||||
});
|
|
||||||
const [ttlConfig, setTTLConfig] = useState({
|
|
||||||
defaultTTL: 5,
|
|
||||||
homeTTL: 5,
|
|
||||||
librariesTTL: 1440,
|
|
||||||
seriesTTL: 5,
|
|
||||||
booksTTL: 5,
|
|
||||||
imagesTTL: 1440,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Charger la configuration existante
|
|
||||||
const savedConfig = storageService.getCredentials();
|
|
||||||
if (savedConfig) {
|
|
||||||
setConfig({
|
|
||||||
serverUrl: savedConfig.serverUrl,
|
|
||||||
username: savedConfig.credentials?.username || "",
|
|
||||||
password: savedConfig.credentials?.password || "",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Charger la configuration des TTL
|
|
||||||
const savedTTLConfig = storageService.getTTLConfig();
|
|
||||||
if (savedTTLConfig) {
|
|
||||||
setTTLConfig(savedTTLConfig);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleClearCache = async () => {
|
|
||||||
setIsCacheClearing(true);
|
|
||||||
setError(null);
|
|
||||||
setSuccess(null);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/komga/cache/clear", {
|
const mongoConfig = await ConfigDBService.getConfig();
|
||||||
method: "POST",
|
// Convertir le document Mongoose en objet simple
|
||||||
});
|
if (mongoConfig) {
|
||||||
|
config = {
|
||||||
if (!response.ok) {
|
url: mongoConfig.url,
|
||||||
const data = await response.json();
|
username: mongoConfig.username,
|
||||||
throw new Error(data.error || "Erreur lors de la suppression du cache");
|
password: mongoConfig.password,
|
||||||
|
userId: mongoConfig.userId,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
toast({
|
|
||||||
title: "Cache supprimé",
|
|
||||||
description: "Cache serveur supprimé avec succès",
|
|
||||||
});
|
|
||||||
router.refresh();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erreur:", error);
|
console.error("Erreur lors de la récupération de la configuration:", error);
|
||||||
toast({
|
// On ne fait rien si la config n'existe pas, on laissera le composant client gérer l'état initial
|
||||||
variant: "destructive",
|
|
||||||
title: "Erreur",
|
|
||||||
description: error instanceof Error ? error.message : "Une erreur est survenue",
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setIsCacheClearing(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTest = async () => {
|
|
||||||
setIsLoading(true);
|
|
||||||
setError(null);
|
|
||||||
setSuccess(null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch("/api/komga/test", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
serverUrl: config.serverUrl,
|
|
||||||
username: config.username,
|
|
||||||
password: config.password,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
throw new Error(data.error || "Erreur lors du test de connexion");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toast({
|
return <ClientSettings initialConfig={config} />;
|
||||||
title: "Connexion réussie",
|
|
||||||
description: "La connexion au serveur Komga a été établie avec succès",
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Erreur:", error);
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: "Erreur",
|
|
||||||
description: error instanceof Error ? error.message : "Une erreur est survenue",
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSave = (event: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
setSuccess(null);
|
|
||||||
|
|
||||||
const formData = new FormData(event.currentTarget);
|
|
||||||
const serverUrl = formData.get("serverUrl") as string;
|
|
||||||
const username = formData.get("username") as string;
|
|
||||||
const password = formData.get("password") as string;
|
|
||||||
|
|
||||||
const newConfig = {
|
|
||||||
serverUrl: serverUrl.trim(),
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
};
|
|
||||||
|
|
||||||
const komgaConfig = {
|
|
||||||
serverUrl: newConfig.serverUrl,
|
|
||||||
credentials: {
|
|
||||||
username: newConfig.username,
|
|
||||||
password: newConfig.password,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
komgaConfigService.setConfig(komgaConfig, true);
|
|
||||||
|
|
||||||
fetch("/api/komga/config", {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(komgaConfig),
|
|
||||||
});
|
|
||||||
|
|
||||||
setConfig(newConfig);
|
|
||||||
|
|
||||||
// Émettre un événement pour notifier les autres composants
|
|
||||||
const configChangeEvent = new CustomEvent("komga-config-changed", { detail: komgaConfig });
|
|
||||||
window.dispatchEvent(configChangeEvent);
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: "Configuration sauvegardée",
|
|
||||||
description: "La configuration a été sauvegardée avec succès",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const { name, value } = event.target;
|
|
||||||
setConfig((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[name]: value,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTTLChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const { name, value } = event.target;
|
|
||||||
setTTLConfig((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[name]: parseInt(value || "0", 10),
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSaveTTL = (event: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
setSuccess(null);
|
|
||||||
|
|
||||||
storageService.setTTLConfig(ttlConfig);
|
|
||||||
toast({
|
|
||||||
title: "Configuration TTL sauvegardée",
|
|
||||||
description: "La configuration des TTL a été sauvegardée avec succès",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="container max-w-3xl mx-auto py-8 space-y-6">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<h1 className="text-3xl font-bold">Préférences</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Messages de succès/erreur */}
|
|
||||||
{error && (
|
|
||||||
<div className="rounded-md bg-destructive/15 p-4">
|
|
||||||
<p className="text-sm text-destructive">{error.message}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{success && (
|
|
||||||
<div className="rounded-md bg-green-500/15 p-4">
|
|
||||||
<p className="text-sm text-green-500">{success}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="grid gap-6">
|
|
||||||
{/* Section Configuration Komga */}
|
|
||||||
<div className="rounded-lg border bg-card text-card-foreground shadow-sm">
|
|
||||||
<div className="p-5 space-y-4">
|
|
||||||
<div>
|
|
||||||
<h2 className="text-xl font-semibold flex items-center gap-2">
|
|
||||||
<Network className="h-5 w-5" />
|
|
||||||
Configuration Komga
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm text-muted-foreground mt-1">
|
|
||||||
Configurez les informations de connexion à votre serveur Komga.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Formulaire de configuration */}
|
|
||||||
<form onSubmit={handleSave} className="space-y-4">
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="serverUrl" className="text-sm font-medium">
|
|
||||||
L'URL du serveur
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="url"
|
|
||||||
id="serverUrl"
|
|
||||||
name="serverUrl"
|
|
||||||
required
|
|
||||||
value={config.serverUrl}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="username" className="text-sm font-medium">
|
|
||||||
L'adresse email de connexion
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="username"
|
|
||||||
name="username"
|
|
||||||
required
|
|
||||||
value={config.username}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="password" className="text-sm font-medium">
|
|
||||||
Mot de passe
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
id="password"
|
|
||||||
name="password"
|
|
||||||
required
|
|
||||||
value={config.password}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-3">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="flex-1 inline-flex items-center justify-center rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
|
|
||||||
>
|
|
||||||
Sauvegarder
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={handleTest}
|
|
||||||
disabled={isLoading}
|
|
||||||
className="flex-1 inline-flex items-center justify-center rounded-md bg-secondary px-3 py-2 text-sm font-medium text-secondary-foreground ring-offset-background transition-colors hover:bg-secondary/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
|
|
||||||
>
|
|
||||||
{isLoading ? (
|
|
||||||
<>
|
|
||||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
||||||
Test en cours...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
"Tester la connexion"
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Section Configuration du Cache */}
|
|
||||||
<div className="rounded-lg border bg-card text-card-foreground shadow-sm">
|
|
||||||
<div className="p-5 space-y-4">
|
|
||||||
<div>
|
|
||||||
<h2 className="text-xl font-semibold flex items-center gap-2">
|
|
||||||
<Trash2 className="h-5 w-5" />
|
|
||||||
Configuration du Cache
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm text-muted-foreground mt-1">
|
|
||||||
Gérez les paramètres de mise en cache des données.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Formulaire TTL */}
|
|
||||||
<form onSubmit={handleSaveTTL} className="space-y-4">
|
|
||||||
<div className="grid gap-3 sm:grid-cols-2">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="defaultTTL" className="text-sm font-medium">
|
|
||||||
TTL par défaut (minutes)
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
id="defaultTTL"
|
|
||||||
name="defaultTTL"
|
|
||||||
min="1"
|
|
||||||
value={ttlConfig.defaultTTL}
|
|
||||||
onChange={handleTTLChange}
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="homeTTL" className="text-sm font-medium">
|
|
||||||
TTL page d'accueil
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
id="homeTTL"
|
|
||||||
name="homeTTL"
|
|
||||||
min="1"
|
|
||||||
value={ttlConfig.homeTTL}
|
|
||||||
onChange={handleTTLChange}
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="librariesTTL" className="text-sm font-medium">
|
|
||||||
TTL bibliothèques
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
id="librariesTTL"
|
|
||||||
name="librariesTTL"
|
|
||||||
min="1"
|
|
||||||
value={ttlConfig.librariesTTL}
|
|
||||||
onChange={handleTTLChange}
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="seriesTTL" className="text-sm font-medium">
|
|
||||||
TTL séries
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
id="seriesTTL"
|
|
||||||
name="seriesTTL"
|
|
||||||
min="1"
|
|
||||||
value={ttlConfig.seriesTTL}
|
|
||||||
onChange={handleTTLChange}
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="booksTTL" className="text-sm font-medium">
|
|
||||||
TTL tomes
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
id="booksTTL"
|
|
||||||
name="booksTTL"
|
|
||||||
min="1"
|
|
||||||
value={ttlConfig.booksTTL}
|
|
||||||
onChange={handleTTLChange}
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="imagesTTL" className="text-sm font-medium">
|
|
||||||
TTL images
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
id="imagesTTL"
|
|
||||||
name="imagesTTL"
|
|
||||||
min="1"
|
|
||||||
value={ttlConfig.imagesTTL}
|
|
||||||
onChange={handleTTLChange}
|
|
||||||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-3">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="flex-1 inline-flex items-center justify-center rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
|
|
||||||
>
|
|
||||||
Sauvegarder les TTL
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={handleClearCache}
|
|
||||||
disabled={isCacheClearing}
|
|
||||||
className="flex-1 inline-flex items-center justify-center rounded-md bg-destructive px-3 py-2 text-sm font-medium text-destructive-foreground ring-offset-background transition-colors hover:bg-destructive/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
|
|
||||||
>
|
|
||||||
{isCacheClearing ? (
|
|
||||||
<>
|
|
||||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
||||||
Suppression...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
"Vider le cache"
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
438
src/components/settings/ClientSettings.tsx
Normal file
438
src/components/settings/ClientSettings.tsx
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { Loader2, Network, Trash2 } from "lucide-react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { storageService } from "@/lib/services/storage.service";
|
||||||
|
import { AuthError } from "@/types/auth";
|
||||||
|
import { useToast } from "@/components/ui/use-toast";
|
||||||
|
import { komgaConfigService } from "@/lib/services/komga-config.service";
|
||||||
|
|
||||||
|
interface ErrorMessage {
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KomgaConfig {
|
||||||
|
url: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ClientSettingsProps {
|
||||||
|
initialConfig: KomgaConfig | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ClientSettings({ initialConfig }: ClientSettingsProps) {
|
||||||
|
console.log("initialConfig", initialConfig);
|
||||||
|
const router = useRouter();
|
||||||
|
const { toast } = useToast();
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [isCacheClearing, setIsCacheClearing] = useState(false);
|
||||||
|
const [error, setError] = useState<AuthError | null>(null);
|
||||||
|
const [success, setSuccess] = useState<string | null>(null);
|
||||||
|
const [config, setConfig] = useState({
|
||||||
|
serverUrl: initialConfig?.url || "",
|
||||||
|
username: initialConfig?.username || "",
|
||||||
|
password: initialConfig?.password || "",
|
||||||
|
});
|
||||||
|
const [ttlConfig, setTTLConfig] = useState({
|
||||||
|
defaultTTL: 5,
|
||||||
|
homeTTL: 5,
|
||||||
|
librariesTTL: 1440,
|
||||||
|
seriesTTL: 5,
|
||||||
|
booksTTL: 5,
|
||||||
|
imagesTTL: 1440,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Charger la configuration des TTL
|
||||||
|
const savedTTLConfig = storageService.getTTLConfig();
|
||||||
|
if (savedTTLConfig) {
|
||||||
|
setTTLConfig(savedTTLConfig);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleClearCache = async () => {
|
||||||
|
setIsCacheClearing(true);
|
||||||
|
setError(null);
|
||||||
|
setSuccess(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/komga/cache/clear", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
throw new Error(data.error || "Erreur lors de la suppression du cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Cache supprimé",
|
||||||
|
description: "Cache serveur supprimé avec succès",
|
||||||
|
});
|
||||||
|
router.refresh();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur:", error);
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Erreur",
|
||||||
|
description: error instanceof Error ? error.message : "Une erreur est survenue",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsCacheClearing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTest = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
setSuccess(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/komga/test", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
serverUrl: config.serverUrl,
|
||||||
|
username: config.username,
|
||||||
|
password: config.password,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
throw new Error(data.error || "Erreur lors du test de connexion");
|
||||||
|
}
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Connexion réussie",
|
||||||
|
description: "La connexion au serveur Komga a été établie avec succès",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur:", error);
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Erreur",
|
||||||
|
description: error instanceof Error ? error.message : "Une erreur est survenue",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setSuccess(null);
|
||||||
|
|
||||||
|
const formData = new FormData(event.currentTarget);
|
||||||
|
const serverUrl = formData.get("serverUrl") as string;
|
||||||
|
const username = formData.get("username") as string;
|
||||||
|
const password = formData.get("password") as string;
|
||||||
|
|
||||||
|
const newConfig = {
|
||||||
|
serverUrl: serverUrl.trim(),
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
};
|
||||||
|
|
||||||
|
const komgaConfig = {
|
||||||
|
serverUrl: newConfig.serverUrl,
|
||||||
|
credentials: {
|
||||||
|
username: newConfig.username,
|
||||||
|
password: newConfig.password,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
komgaConfigService.setConfig(komgaConfig, true);
|
||||||
|
|
||||||
|
fetch("/api/komga/config", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
url: newConfig.serverUrl,
|
||||||
|
username: newConfig.username,
|
||||||
|
password: newConfig.password,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
setConfig(newConfig);
|
||||||
|
|
||||||
|
// Émettre un événement pour notifier les autres composants
|
||||||
|
const configChangeEvent = new CustomEvent("komga-config-changed", { detail: komgaConfig });
|
||||||
|
window.dispatchEvent(configChangeEvent);
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Configuration sauvegardée",
|
||||||
|
description: "La configuration a été sauvegardée avec succès",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const { name, value } = event.target;
|
||||||
|
setConfig((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[name]: value,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTTLChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const { name, value } = event.target;
|
||||||
|
setTTLConfig((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[name]: parseInt(value || "0", 10),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveTTL = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setSuccess(null);
|
||||||
|
|
||||||
|
storageService.setTTLConfig(ttlConfig);
|
||||||
|
toast({
|
||||||
|
title: "Configuration TTL sauvegardée",
|
||||||
|
description: "La configuration des TTL a été sauvegardée avec succès",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container max-w-3xl mx-auto py-8 space-y-6">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h1 className="text-3xl font-bold">Préférences</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Messages de succès/erreur */}
|
||||||
|
{error && (
|
||||||
|
<div className="rounded-md bg-destructive/15 p-4">
|
||||||
|
<p className="text-sm text-destructive">{error.message}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{success && (
|
||||||
|
<div className="rounded-md bg-green-500/15 p-4">
|
||||||
|
<p className="text-sm text-green-500">{success}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="grid gap-6">
|
||||||
|
{/* Section Configuration Komga */}
|
||||||
|
<div className="rounded-lg border bg-card text-card-foreground shadow-sm">
|
||||||
|
<div className="p-5 space-y-4">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-semibold flex items-center gap-2">
|
||||||
|
<Network className="h-5 w-5" />
|
||||||
|
Configuration Komga
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
|
Configurez les informations de connexion à votre serveur Komga.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Formulaire de configuration */}
|
||||||
|
<form onSubmit={handleSave} className="space-y-4">
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label htmlFor="serverUrl" className="text-sm font-medium">
|
||||||
|
L'URL du serveur
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
id="serverUrl"
|
||||||
|
name="serverUrl"
|
||||||
|
required
|
||||||
|
value={config.serverUrl}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label htmlFor="username" className="text-sm font-medium">
|
||||||
|
L'adresse email de connexion
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
required
|
||||||
|
value={config.username}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label htmlFor="password" className="text-sm font-medium">
|
||||||
|
Mot de passe
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
required
|
||||||
|
value={config.password}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="flex-1 inline-flex items-center justify-center rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
|
||||||
|
>
|
||||||
|
Sauvegarder
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleTest}
|
||||||
|
disabled={isLoading}
|
||||||
|
className="flex-1 inline-flex items-center justify-center rounded-md bg-secondary px-3 py-2 text-sm font-medium text-secondary-foreground ring-offset-background transition-colors hover:bg-secondary/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
Test en cours...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
"Tester la connexion"
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Section Configuration du Cache */}
|
||||||
|
<div className="rounded-lg border bg-card text-card-foreground shadow-sm">
|
||||||
|
<div className="p-5 space-y-4">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-semibold flex items-center gap-2">
|
||||||
|
<Trash2 className="h-5 w-5" />
|
||||||
|
Configuration du Cache
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
|
Gérez les paramètres de mise en cache des données.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Formulaire TTL */}
|
||||||
|
<form onSubmit={handleSaveTTL} className="space-y-4">
|
||||||
|
<div className="grid gap-3 sm:grid-cols-2">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label htmlFor="defaultTTL" className="text-sm font-medium">
|
||||||
|
TTL par défaut (minutes)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="defaultTTL"
|
||||||
|
name="defaultTTL"
|
||||||
|
min="1"
|
||||||
|
value={ttlConfig.defaultTTL}
|
||||||
|
onChange={handleTTLChange}
|
||||||
|
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label htmlFor="homeTTL" className="text-sm font-medium">
|
||||||
|
TTL page d'accueil
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="homeTTL"
|
||||||
|
name="homeTTL"
|
||||||
|
min="1"
|
||||||
|
value={ttlConfig.homeTTL}
|
||||||
|
onChange={handleTTLChange}
|
||||||
|
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label htmlFor="librariesTTL" className="text-sm font-medium">
|
||||||
|
TTL bibliothèques
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="librariesTTL"
|
||||||
|
name="librariesTTL"
|
||||||
|
min="1"
|
||||||
|
value={ttlConfig.librariesTTL}
|
||||||
|
onChange={handleTTLChange}
|
||||||
|
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label htmlFor="seriesTTL" className="text-sm font-medium">
|
||||||
|
TTL séries
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="seriesTTL"
|
||||||
|
name="seriesTTL"
|
||||||
|
min="1"
|
||||||
|
value={ttlConfig.seriesTTL}
|
||||||
|
onChange={handleTTLChange}
|
||||||
|
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label htmlFor="booksTTL" className="text-sm font-medium">
|
||||||
|
TTL tomes
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="booksTTL"
|
||||||
|
name="booksTTL"
|
||||||
|
min="1"
|
||||||
|
value={ttlConfig.booksTTL}
|
||||||
|
onChange={handleTTLChange}
|
||||||
|
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label htmlFor="imagesTTL" className="text-sm font-medium">
|
||||||
|
TTL images
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="imagesTTL"
|
||||||
|
name="imagesTTL"
|
||||||
|
min="1"
|
||||||
|
value={ttlConfig.imagesTTL}
|
||||||
|
onChange={handleTTLChange}
|
||||||
|
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="flex-1 inline-flex items-center justify-center rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground ring-offset-background transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
|
||||||
|
>
|
||||||
|
Sauvegarder les TTL
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleClearCache}
|
||||||
|
disabled={isCacheClearing}
|
||||||
|
className="flex-1 inline-flex items-center justify-center rounded-md bg-destructive px-3 py-2 text-sm font-medium text-destructive-foreground ring-offset-background transition-colors hover:bg-destructive/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{isCacheClearing ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
Suppression...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
"Vider le cache"
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
35
src/lib/models/config.model.ts
Normal file
35
src/lib/models/config.model.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
const configSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
userId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Middleware pour mettre à jour le champ updatedAt avant la sauvegarde
|
||||||
|
configSchema.pre("save", function (next) {
|
||||||
|
this.updatedAt = new Date();
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
export const KomgaConfig =
|
||||||
|
mongoose.models.KomgaConfig || mongoose.model("KomgaConfig", configSchema);
|
||||||
51
src/lib/mongodb.ts
Normal file
51
src/lib/mongodb.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
const MONGODB_URI = process.env.MONGODB_URI;
|
||||||
|
|
||||||
|
if (!MONGODB_URI) {
|
||||||
|
throw new Error(
|
||||||
|
"Veuillez définir la variable d'environnement MONGODB_URI dans votre fichier .env"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MongooseCache {
|
||||||
|
conn: typeof mongoose | null;
|
||||||
|
promise: Promise<typeof mongoose> | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var mongoose: MongooseCache | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cached: MongooseCache = global.mongoose || { conn: null, promise: null };
|
||||||
|
|
||||||
|
if (!global.mongoose) {
|
||||||
|
global.mongoose = { conn: null, promise: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function connectDB(): Promise<typeof mongoose> {
|
||||||
|
if (cached.conn) {
|
||||||
|
return cached.conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cached.promise) {
|
||||||
|
const opts = {
|
||||||
|
bufferCommands: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
cached.promise = mongoose.connect(MONGODB_URI!, opts).then((mongoose) => {
|
||||||
|
return mongoose;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
cached.conn = await cached.promise;
|
||||||
|
} catch (e) {
|
||||||
|
cached.promise = null;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cached.conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connectDB;
|
||||||
62
src/lib/services/config-db.service.ts
Normal file
62
src/lib/services/config-db.service.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { cookies } from "next/headers";
|
||||||
|
import connectDB from "@/lib/mongodb";
|
||||||
|
import { KomgaConfig } from "@/lib/models/config.model";
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KomgaConfigData {
|
||||||
|
url: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ConfigDBService {
|
||||||
|
private static async getCurrentUser(): Promise<User> {
|
||||||
|
const userCookie = cookies().get("stripUser");
|
||||||
|
|
||||||
|
if (!userCookie) {
|
||||||
|
throw new Error("Utilisateur non authentifié");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(atob(userCookie.value));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur lors de la récupération de l'utilisateur depuis le cookie:", error);
|
||||||
|
throw new Error("Utilisateur non authentifié");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async saveConfig(data: KomgaConfigData) {
|
||||||
|
const user = await this.getCurrentUser();
|
||||||
|
await connectDB();
|
||||||
|
|
||||||
|
const config = await KomgaConfig.findOneAndUpdate(
|
||||||
|
{ userId: user.id },
|
||||||
|
{
|
||||||
|
userId: user.id,
|
||||||
|
url: data.url,
|
||||||
|
username: data.username,
|
||||||
|
password: data.password,
|
||||||
|
},
|
||||||
|
{ upsert: true, new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getConfig() {
|
||||||
|
const user = await this.getCurrentUser();
|
||||||
|
await connectDB();
|
||||||
|
|
||||||
|
const config = await KomgaConfig.findOne({ userId: user.id });
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
throw new Error("Configuration non trouvée");
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user