Add some features to the admin panel
This commit is contained in:
194
admin-panel/bun.lock
Normal file
194
admin-panel/bun.lock
Normal file
@ -0,0 +1,194 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "admin-panel",
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"@vitejs/plugin-react": "^4.0.3",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.4.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
||||
|
||||
"@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
||||
|
||||
"@babel/compat-data": ["@babel/compat-data@7.27.5", "", {}, "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg=="],
|
||||
|
||||
"@babel/core": ["@babel/core@7.27.4", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.4", "@babel/parser": "^7.27.4", "@babel/template": "^7.27.2", "@babel/traverse": "^7.27.4", "@babel/types": "^7.27.3", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g=="],
|
||||
|
||||
"@babel/generator": ["@babel/generator@7.27.5", "", { "dependencies": { "@babel/parser": "^7.27.5", "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw=="],
|
||||
|
||||
"@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
|
||||
|
||||
"@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
|
||||
|
||||
"@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.27.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.27.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg=="],
|
||||
|
||||
"@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="],
|
||||
|
||||
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
|
||||
|
||||
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
||||
|
||||
"@babel/helpers": ["@babel/helpers@7.27.6", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.27.6" } }, "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug=="],
|
||||
|
||||
"@babel/parser": ["@babel/parser@7.27.5", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg=="],
|
||||
|
||||
"@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
|
||||
|
||||
"@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
|
||||
|
||||
"@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
|
||||
|
||||
"@babel/traverse": ["@babel/traverse@7.27.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.3", "@babel/parser": "^7.27.4", "@babel/template": "^7.27.2", "@babel/types": "^7.27.3", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA=="],
|
||||
|
||||
"@babel/types": ["@babel/types@7.27.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
|
||||
"@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||
|
||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.9", "", {}, "sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w=="],
|
||||
|
||||
"@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
|
||||
|
||||
"@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
|
||||
|
||||
"@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
|
||||
|
||||
"@types/babel__traverse": ["@types/babel__traverse@7.20.7", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng=="],
|
||||
|
||||
"@types/prop-types": ["@types/prop-types@15.7.14", "", {}, "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="],
|
||||
|
||||
"@types/react": ["@types/react@18.3.23", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w=="],
|
||||
|
||||
"@types/react-dom": ["@types/react-dom@18.3.7", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="],
|
||||
|
||||
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.5.1", "", { "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@rolldown/pluginutils": "1.0.0-beta.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "sha512-uPZBqSI0YD4lpkIru6M35sIfylLGTyhGHvDZbNLuMA73lMlwJKz5xweH7FajfcCAc2HnINciejA9qTz0dr0M7A=="],
|
||||
|
||||
"browserslist": ["browserslist@4.25.0", "", { "dependencies": { "caniuse-lite": "^1.0.30001718", "electron-to-chromium": "^1.5.160", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001721", "", {}, "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ=="],
|
||||
|
||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.165", "", {}, "sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw=="],
|
||||
|
||||
"esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
||||
|
||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
|
||||
"globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
|
||||
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
||||
|
||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"postcss": ["postcss@8.5.4", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="],
|
||||
|
||||
"react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
|
||||
|
||||
"react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="],
|
||||
|
||||
"react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
|
||||
|
||||
"rollup": ["rollup@3.29.5", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w=="],
|
||||
|
||||
"scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
|
||||
|
||||
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||
|
||||
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
|
||||
|
||||
"vite": ["vite@4.5.14", "", { "dependencies": { "esbuild": "^0.18.10", "postcss": "^8.4.27", "rollup": "^3.27.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g=="],
|
||||
|
||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
}
|
||||
}
|
@ -15,6 +15,14 @@ interface Stats {
|
||||
activeFeeds: number;
|
||||
inactiveFeeds: number;
|
||||
totalEpisodes: number;
|
||||
pendingRequests: number;
|
||||
totalRequests: number;
|
||||
batchScheduler: {
|
||||
enabled: boolean;
|
||||
isRunning: boolean;
|
||||
lastRun?: string;
|
||||
nextRun?: string;
|
||||
};
|
||||
lastUpdated: string;
|
||||
adminPort: number;
|
||||
authEnabled: boolean;
|
||||
@ -32,7 +40,7 @@ function App() {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState<string | null>(null);
|
||||
const [newFeedUrl, setNewFeedUrl] = useState('');
|
||||
const [activeTab, setActiveTab] = useState<'dashboard' | 'feeds' | 'env'>('dashboard');
|
||||
const [activeTab, setActiveTab] = useState<'dashboard' | 'feeds' | 'env' | 'batch'>('dashboard');
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
@ -151,6 +159,7 @@ function App() {
|
||||
|
||||
if (res.ok) {
|
||||
setSuccess(data.message);
|
||||
loadData(); // Refresh data to update batch status
|
||||
} else {
|
||||
setError(data.error || 'バッチ処理開始に失敗しました');
|
||||
}
|
||||
@ -160,6 +169,26 @@ function App() {
|
||||
}
|
||||
};
|
||||
|
||||
const toggleBatchScheduler = async (enable: boolean) => {
|
||||
try {
|
||||
const res = await fetch(`/api/admin/batch/${enable ? 'enable' : 'disable'}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (res.ok) {
|
||||
setSuccess(data.message);
|
||||
loadData(); // Refresh data to update batch status
|
||||
} else {
|
||||
setError(data.error || 'バッチスケジューラーの状態変更に失敗しました');
|
||||
}
|
||||
} catch (err) {
|
||||
setError('バッチスケジューラーの状態変更に失敗しました');
|
||||
console.error('Error toggling batch scheduler:', err);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <div className="container"><div className="loading">読み込み中...</div></div>;
|
||||
}
|
||||
@ -189,6 +218,12 @@ function App() {
|
||||
>
|
||||
フィード管理
|
||||
</button>
|
||||
<button
|
||||
className={`btn ${activeTab === 'batch' ? 'btn-primary' : 'btn-secondary'}`}
|
||||
onClick={() => setActiveTab('batch')}
|
||||
>
|
||||
バッチ管理
|
||||
</button>
|
||||
<button
|
||||
className={`btn ${activeTab === 'env' ? 'btn-primary' : 'btn-secondary'}`}
|
||||
onClick={() => setActiveTab('env')}
|
||||
@ -218,6 +253,16 @@ function App() {
|
||||
<div className="value">{stats?.totalEpisodes || 0}</div>
|
||||
<div className="label">総エピソード数</div>
|
||||
</div>
|
||||
<div className="stat-card">
|
||||
<div className="value">{stats?.pendingRequests || 0}</div>
|
||||
<div className="label">保留中リクエスト</div>
|
||||
</div>
|
||||
<div className="stat-card">
|
||||
<div className="value" style={{ color: stats?.batchScheduler?.enabled ? '#28a745' : '#dc3545' }}>
|
||||
{stats?.batchScheduler?.enabled ? 'ON' : 'OFF'}
|
||||
</div>
|
||||
<div className="label">バッチスケジューラー</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: '20px' }}>
|
||||
@ -296,6 +341,93 @@ function App() {
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeTab === 'batch' && (
|
||||
<>
|
||||
<h3>バッチ処理管理</h3>
|
||||
<p style={{ marginBottom: '20px', color: '#7f8c8d' }}>
|
||||
定期バッチ処理の状態管理と手動実行を行えます。
|
||||
</p>
|
||||
|
||||
<div className="stats-grid" style={{ marginBottom: '24px' }}>
|
||||
<div className="stat-card">
|
||||
<div className="value" style={{ color: stats?.batchScheduler?.enabled ? '#28a745' : '#dc3545' }}>
|
||||
{stats?.batchScheduler?.enabled ? '有効' : '無効'}
|
||||
</div>
|
||||
<div className="label">スケジューラー状態</div>
|
||||
</div>
|
||||
<div className="stat-card">
|
||||
<div className="value" style={{ color: stats?.batchScheduler?.isRunning ? '#ffc107' : '#6c757d' }}>
|
||||
{stats?.batchScheduler?.isRunning ? '実行中' : '待機中'}
|
||||
</div>
|
||||
<div className="label">実行状態</div>
|
||||
</div>
|
||||
<div className="stat-card">
|
||||
<div className="value" style={{ fontSize: '12px' }}>
|
||||
{stats?.batchScheduler?.lastRun
|
||||
? new Date(stats.batchScheduler.lastRun).toLocaleString('ja-JP')
|
||||
: '未実行'}
|
||||
</div>
|
||||
<div className="label">前回実行</div>
|
||||
</div>
|
||||
<div className="stat-card">
|
||||
<div className="value" style={{ fontSize: '12px' }}>
|
||||
{stats?.batchScheduler?.nextRun
|
||||
? new Date(stats.batchScheduler.nextRun).toLocaleString('ja-JP')
|
||||
: '未予定'}
|
||||
</div>
|
||||
<div className="label">次回実行</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: '24px' }}>
|
||||
<h4>スケジューラー制御</h4>
|
||||
<div style={{ display: 'flex', gap: '12px', marginTop: '12px' }}>
|
||||
<button
|
||||
className="btn btn-success"
|
||||
onClick={() => toggleBatchScheduler(true)}
|
||||
disabled={stats?.batchScheduler?.enabled}
|
||||
>
|
||||
スケジューラーを有効化
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-warning"
|
||||
onClick={() => toggleBatchScheduler(false)}
|
||||
disabled={!stats?.batchScheduler?.enabled}
|
||||
>
|
||||
スケジューラーを無効化
|
||||
</button>
|
||||
</div>
|
||||
<p style={{ fontSize: '14px', color: '#6c757d', marginTop: '8px' }}>
|
||||
スケジューラーを無効化すると、定期的なバッチ処理が停止します。手動実行は引き続き可能です。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: '24px' }}>
|
||||
<h4>手動実行</h4>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={triggerBatch}
|
||||
disabled={stats?.batchScheduler?.isRunning}
|
||||
>
|
||||
{stats?.batchScheduler?.isRunning ? 'バッチ処理実行中...' : 'バッチ処理を手動実行'}
|
||||
</button>
|
||||
<p style={{ fontSize: '14px', color: '#6c757d', marginTop: '8px' }}>
|
||||
スケジューラーの状態に関係なく、バッチ処理を手動で実行できます。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: '16px', background: '#f8f9fa', borderRadius: '4px' }}>
|
||||
<h4>バッチ処理について</h4>
|
||||
<ul style={{ fontSize: '14px', color: '#6c757d', marginTop: '8px', paddingLeft: '20px' }}>
|
||||
<li>定期バッチ処理は6時間ごとに実行されます</li>
|
||||
<li>新しいRSS記事の取得、要約生成、音声合成を行います</li>
|
||||
<li>スケジューラーが無効の場合、定期実行は停止しますが手動実行は可能です</li>
|
||||
<li>バッチ処理の実行中は重複実行を防ぐため、新たな実行はスキップされます</li>
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeTab === 'env' && (
|
||||
<>
|
||||
<h3>環境変数設定</h3>
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
updateFeedRequestStatus,
|
||||
} from "./services/database.js";
|
||||
import { batchProcess, addNewFeedUrl } from "./scripts/fetch_and_generate.js";
|
||||
import { batchScheduler } from "./services/batch-scheduler.js";
|
||||
|
||||
// Validate configuration on startup
|
||||
try {
|
||||
@ -284,12 +285,52 @@ app.patch("/api/admin/feed-requests/:id/reject", async (c) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Batch scheduler management
|
||||
app.get("/api/admin/batch/status", async (c) => {
|
||||
try {
|
||||
const status = batchScheduler.getStatus();
|
||||
return c.json(status);
|
||||
} catch (error) {
|
||||
console.error("Error fetching batch status:", error);
|
||||
return c.json({ error: "Failed to fetch batch status" }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/admin/batch/enable", async (c) => {
|
||||
try {
|
||||
batchScheduler.enable();
|
||||
return c.json({
|
||||
success: true,
|
||||
message: "Batch scheduler enabled successfully",
|
||||
status: batchScheduler.getStatus()
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error enabling batch scheduler:", error);
|
||||
return c.json({ error: "Failed to enable batch scheduler" }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/admin/batch/disable", async (c) => {
|
||||
try {
|
||||
batchScheduler.disable();
|
||||
return c.json({
|
||||
success: true,
|
||||
message: "Batch scheduler disabled successfully",
|
||||
status: batchScheduler.getStatus()
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error disabling batch scheduler:", error);
|
||||
return c.json({ error: "Failed to disable batch scheduler" }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// System management
|
||||
app.get("/api/admin/stats", async (c) => {
|
||||
try {
|
||||
const feeds = await getAllFeedsIncludingInactive();
|
||||
const episodes = await fetchAllEpisodes();
|
||||
const feedRequests = await getFeedRequests();
|
||||
const batchStatus = batchScheduler.getStatus();
|
||||
|
||||
const stats = {
|
||||
totalFeeds: feeds.length,
|
||||
@ -298,6 +339,12 @@ app.get("/api/admin/stats", async (c) => {
|
||||
totalEpisodes: episodes.length,
|
||||
pendingRequests: feedRequests.filter(r => r.status === 'pending').length,
|
||||
totalRequests: feedRequests.length,
|
||||
batchScheduler: {
|
||||
enabled: batchStatus.enabled,
|
||||
isRunning: batchStatus.isRunning,
|
||||
lastRun: batchStatus.lastRun,
|
||||
nextRun: batchStatus.nextRun,
|
||||
},
|
||||
lastUpdated: new Date().toISOString(),
|
||||
adminPort: config.admin.port,
|
||||
authEnabled: !!(config.admin.username && config.admin.password),
|
||||
@ -314,8 +361,8 @@ app.post("/api/admin/batch/trigger", async (c) => {
|
||||
try {
|
||||
console.log("🚀 Manual batch process triggered via admin panel");
|
||||
|
||||
// Run batch process in background
|
||||
runBatchProcess().catch(error => {
|
||||
// Use the batch scheduler's manual trigger method
|
||||
batchScheduler.triggerManualRun().catch(error => {
|
||||
console.error("❌ Manual admin batch process failed:", error);
|
||||
});
|
||||
|
||||
|
50
server.ts
50
server.ts
@ -2,7 +2,7 @@ import { Hono } from "hono";
|
||||
import { serve } from "@hono/node-server";
|
||||
import path from "path";
|
||||
import { config, validateConfig } from "./services/config.js";
|
||||
import { batchProcess } from "./scripts/fetch_and_generate.js";
|
||||
import { batchScheduler } from "./services/batch-scheduler.js";
|
||||
|
||||
// Validate configuration on startup
|
||||
try {
|
||||
@ -148,47 +148,8 @@ app.post("/api/feed-requests", async (c) => {
|
||||
// Catch-all for SPA routing
|
||||
app.get("*", serveIndex);
|
||||
|
||||
// Batch processing functions
|
||||
function scheduleFirstBatchProcess() {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
console.log("🚀 Running initial batch process...");
|
||||
await runBatchProcess();
|
||||
console.log("✅ Initial batch process completed");
|
||||
} catch (error) {
|
||||
console.error("❌ Error during initial batch process:", error);
|
||||
}
|
||||
}, 10000); // Wait 10 seconds after startup
|
||||
}
|
||||
|
||||
function scheduleSixHourlyBatchProcess() {
|
||||
const SIX_HOURS_MS = 6 * 60 * 60 * 1000; // 6 hours in milliseconds
|
||||
|
||||
console.log(
|
||||
`🕕 Next batch process scheduled in 6 hours (${new Date(Date.now() + SIX_HOURS_MS).toLocaleString()})`
|
||||
);
|
||||
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
console.log("🔄 Running scheduled 6-hourly batch process...");
|
||||
await runBatchProcess();
|
||||
console.log("✅ Scheduled batch process completed");
|
||||
} catch (error) {
|
||||
console.error("❌ Error during scheduled batch process:", error);
|
||||
}
|
||||
// Schedule next run
|
||||
scheduleSixHourlyBatchProcess();
|
||||
}, SIX_HOURS_MS);
|
||||
}
|
||||
|
||||
async function runBatchProcess(): Promise<void> {
|
||||
try {
|
||||
await batchProcess();
|
||||
} catch (error) {
|
||||
console.error("Batch process failed:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
// Batch processing - now using batch scheduler
|
||||
console.log("🔄 Batch scheduler initialized and ready");
|
||||
|
||||
// サーバー起動
|
||||
serve(
|
||||
@ -200,9 +161,6 @@ serve(
|
||||
console.log(`🌟 Server is running on http://localhost:${info.port}`);
|
||||
console.log(`📡 Using configuration from: ${config.paths.projectRoot}`);
|
||||
console.log(`🗄️ Database: ${config.paths.dbPath}`);
|
||||
|
||||
// Schedule batch processes
|
||||
scheduleFirstBatchProcess();
|
||||
scheduleSixHourlyBatchProcess();
|
||||
console.log(`🔄 Batch scheduler is active and will manage automatic processing`);
|
||||
},
|
||||
);
|
||||
|
132
services/batch-scheduler.ts
Normal file
132
services/batch-scheduler.ts
Normal file
@ -0,0 +1,132 @@
|
||||
import { batchProcess } from "../scripts/fetch_and_generate.js";
|
||||
|
||||
interface BatchSchedulerState {
|
||||
enabled: boolean;
|
||||
lastRun?: string;
|
||||
nextRun?: string;
|
||||
isRunning: boolean;
|
||||
intervalId?: NodeJS.Timeout;
|
||||
}
|
||||
|
||||
class BatchScheduler {
|
||||
private state: BatchSchedulerState = {
|
||||
enabled: true,
|
||||
isRunning: false,
|
||||
};
|
||||
|
||||
private readonly SIX_HOURS_MS = 6 * 60 * 60 * 1000; // 6 hours in milliseconds
|
||||
|
||||
constructor() {
|
||||
// Start with initial delay and then schedule regular runs
|
||||
this.scheduleInitialRun();
|
||||
}
|
||||
|
||||
private scheduleInitialRun() {
|
||||
setTimeout(async () => {
|
||||
if (this.state.enabled) {
|
||||
await this.runBatch();
|
||||
}
|
||||
if (this.state.enabled) {
|
||||
this.scheduleRegularRuns();
|
||||
}
|
||||
}, 10000); // Wait 10 seconds after startup
|
||||
}
|
||||
|
||||
private scheduleRegularRuns() {
|
||||
if (this.state.intervalId) {
|
||||
clearTimeout(this.state.intervalId);
|
||||
}
|
||||
|
||||
if (!this.state.enabled) {
|
||||
this.state.nextRun = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const nextRunTime = Date.now() + this.SIX_HOURS_MS;
|
||||
this.state.nextRun = new Date(nextRunTime).toISOString();
|
||||
|
||||
console.log(
|
||||
`🕕 Next batch process scheduled for: ${new Date(nextRunTime).toLocaleString()}`
|
||||
);
|
||||
|
||||
this.state.intervalId = setTimeout(async () => {
|
||||
if (this.state.enabled) {
|
||||
await this.runBatch();
|
||||
this.scheduleRegularRuns(); // Schedule next run
|
||||
}
|
||||
}, this.SIX_HOURS_MS);
|
||||
}
|
||||
|
||||
private async runBatch(): Promise<void> {
|
||||
if (this.state.isRunning) {
|
||||
console.log("⚠️ Batch process already running, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.isRunning = true;
|
||||
this.state.lastRun = new Date().toISOString();
|
||||
|
||||
try {
|
||||
console.log("🔄 Running scheduled batch process...");
|
||||
await batchProcess();
|
||||
console.log("✅ Scheduled batch process completed");
|
||||
} catch (error) {
|
||||
console.error("❌ Error during scheduled batch process:", error);
|
||||
} finally {
|
||||
this.state.isRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async triggerManualRun(): Promise<void> {
|
||||
console.log("🚀 Manual batch process triggered");
|
||||
await this.runBatch();
|
||||
}
|
||||
|
||||
public enable(): void {
|
||||
if (this.state.enabled) {
|
||||
console.log("ℹ️ Batch scheduler already enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.enabled = true;
|
||||
console.log("✅ Batch scheduler enabled");
|
||||
this.scheduleRegularRuns();
|
||||
}
|
||||
|
||||
public disable(): void {
|
||||
if (!this.state.enabled) {
|
||||
console.log("ℹ️ Batch scheduler already disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.enabled = false;
|
||||
console.log("⏸️ Batch scheduler disabled");
|
||||
|
||||
if (this.state.intervalId) {
|
||||
clearTimeout(this.state.intervalId);
|
||||
this.state.intervalId = undefined;
|
||||
}
|
||||
|
||||
this.state.nextRun = undefined;
|
||||
}
|
||||
|
||||
public getStatus(): BatchSchedulerState {
|
||||
return {
|
||||
...this.state,
|
||||
intervalId: undefined, // Don't expose the timeout ID
|
||||
};
|
||||
}
|
||||
|
||||
public isEnabled(): boolean {
|
||||
return this.state.enabled;
|
||||
}
|
||||
|
||||
public isRunning(): boolean {
|
||||
return this.state.isRunning;
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const batchScheduler = new BatchScheduler();
|
||||
|
||||
export type { BatchSchedulerState };
|
Reference in New Issue
Block a user