From 03229f23d40f015a33ffc8bee70ab996e4a4c14c Mon Sep 17 00:00:00 2001 From: Lenheart <947330670@qq.com> Date: Wed, 15 Apr 2026 15:19:28 +0800 Subject: [PATCH] 1.0 --- .vscode/compile_commands.json | 131 ++ .xmake/windows/x64/cache/config | 9 + .xmake/windows/x64/cache/detect | 128 ++ .xmake/windows/x64/cache/history | 67 + .xmake/windows/x64/cache/option | 1 + .xmake/windows/x64/cache/package | 87 + .xmake/windows/x64/cache/project | 1 + .xmake/windows/x64/cache/references | 9 + .xmake/windows/x64/cache/repository | 13 + .xmake/windows/x64/cache/toolchain | 219 +++ .xmake/windows/x64/project.lock | 0 .xmake/windows/x64/xmake.conf | 38 + README.md | 75 + build-debug-rebuild.log | Bin 0 -> 20636 bytes build-debug.log | Bin 0 -> 762 bytes config/server.conf | 34 + config/server.conf.example | 33 + config/server.windows.conf | 33 + folder-alias.json | 1 + http-log-body-test.err | 1 + http-log-body-test.out | 0 http-log-test.err | 3 + http-log-test.out | 0 include/dps/app.hpp | 227 +++ include/dps/config.hpp | 62 + include/dps/fs_utils.hpp | 14 + include/dps/http.hpp | 73 + include/dps/json.hpp | 72 + include/dps/labels.hpp | 9 + packaging/systemd/dps-manager-server.service | 15 + private-folder-alias.json | 1 + server-run.err | 4 + server-run.out | 0 server-start-check.err | 1 + server-start-check.out | 0 server-unicode-check.err | 6 + server-unicode-check.out | 0 src/app.cpp | 1586 +++++++++++++++++ src/config.cpp | 117 ++ src/fs_utils.cpp | 33 + src/http.cpp | 534 ++++++ src/json.cpp | 384 ++++ src/labels.cpp | 17 + src/main.cpp | 25 + tests/config_sync_test.py | 51 + tests/crypto_smoke_test.cpp | 162 ++ tests/fs_utils_smoke_test.cpp | 44 + tests/get_info_level_contract_test.py | 31 + tests/json_smoke_test.cpp | 18 + tests/labels_smoke_test.cpp | 25 + tmp-verify.err | 12 + tmp-verify.out | 0 xmake.lua | 103 ++ 旧的java项目/.gitignore | 33 + 旧的java项目/.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 58727 bytes .../.mvn/wrapper/maven-wrapper.properties | 2 + 旧的java项目/AGENTS.md | 120 ++ 旧的java项目/itcast/client/ChatClient.java | 41 + .../itcast/message/AbstractResponseMessage.java | 19 + .../itcast/message/ChatRequestMessage.java | 26 + .../itcast/message/ChatResponseMessage.java | 26 + .../itcast/message/GroupChatRequestMessage.java | 23 + .../itcast/message/GroupChatResponseMessage.java | 24 + .../itcast/message/GroupCreateRequestMessage.java | 23 + .../message/GroupCreateResponseMessage.java | 18 + .../itcast/message/GroupJoinRequestMessage.java | 22 + .../itcast/message/GroupJoinResponseMessage.java | 18 + .../message/GroupMembersRequestMessage.java | 19 + .../message/GroupMembersResponseMessage.java | 22 + .../itcast/message/GroupQuitRequestMessage.java | 22 + .../itcast/message/GroupQuitResponseMessage.java | 17 + .../itcast/message/LoginRequestMessage.java | 26 + .../itcast/message/LoginResponseMessage.java | 13 + 旧的java项目/itcast/message/Message.java | 54 + .../itcast/protocol/MessageCodec.java | 62 + .../itcast/protocol/MessageCodecSharable.java | 66 + .../itcast/protocol/ProcotolFrameDecoder.java | 14 + 旧的java项目/itcast/server/ChatServer.java | 44 + .../itcast/server/service/UserService.java | 15 + .../itcast/server/service/UserServiceFactory.java | 10 + .../server/service/UserServiceMemoryImpl.java | 25 + 旧的java项目/itcast/server/session/Group.java | 24 + .../itcast/server/session/GroupSession.java | 57 + .../server/session/GroupSessionFactory.java | 10 + .../server/session/GroupSessionMemoryImpl.java | 55 + .../itcast/server/session/Session.java | 46 + .../itcast/server/session/SessionFactory.java | 10 + .../itcast/server/session/SessionMemoryImpl.java | 47 + 旧的java项目/mvnw | 316 ++++ 旧的java项目/mvnw.cmd | 188 ++ 旧的java项目/pom.xml | 134 ++ .../com/example/sjkbf/ApplicationRunnerImpl.java | 27 + .../java/com/example/sjkbf/CodeGenerator.java | 141 ++ .../java/com/example/sjkbf/SaticScheduleTask.java | 110 ++ .../java/com/example/sjkbf/SjkbfApplication.java | 59 + .../java/com/example/sjkbf/common/FileManage.java | 41 + .../sjkbf/config/Configuration/CurrentUserId.java | 11 + .../sjkbf/config/Configuration/WebMvcConfig.java | 43 + .../java/com/example/sjkbf/config/CorsConfig.java | 44 + .../config/JwtAuthenticationTokenFilter.java | 87 + .../example/sjkbf/config/MyMetaObjectHandler.java | 26 + .../com/example/sjkbf/config/RateLimitConfig.java | 16 + .../com/example/sjkbf/config/SecurityConfig.java | 134 ++ .../java/com/example/sjkbf/config/StartInit.java | 25 + .../com/example/sjkbf/config/VideoConfig.java | 31 + .../exception/CustomAuthenticationEntryPoint.java | 36 + .../config/exception/GlobalExceptionHandler.java | 70 + .../sjkbf/controller/AccountCargoController.java | 328 ++++ .../example/sjkbf/controller/AuthController.java | 103 ++ .../example/sjkbf/controller/UserController.java | 699 ++++++++ .../controller/UserServerProjectController.java | 22 + .../example/sjkbf/controller/VideoController.java | 344 ++++ .../example/sjkbf/controller/ZfController.java | 73 + .../sjkbf/controller/verifyController.java | 729 ++++++++ .../java/com/example/sjkbf/entity/AuthUser.java | 16 + .../com/example/sjkbf/entity/CommonResult.java | 118 ++ .../com/example/sjkbf/entity/DPSScriptJson.java | 21 + .../example/sjkbf/entity/DTO/LoginRequest.java | 16 + .../example/sjkbf/entity/DTO/LoginResponse.java | 18 + .../main/java/com/example/sjkbf/entity/Log.java | 34 + .../java/com/example/sjkbf/entity/ProjectPer.java | 31 + .../main/java/com/example/sjkbf/entity/User.java | 51 + .../com/example/sjkbf/entity/UserAndServer.java | 33 + .../example/sjkbf/entity/UserServerProject.java | 33 + .../sjkbf/entity/projectconfig/Project.java | 31 + .../sjkbf/entity/projectconfig/ProjectConfig.java | 26 + .../java/com/example/sjkbf/mapper/LogMapper.java | 18 + .../example/sjkbf/mapper/ProjectPerMapper.java | 18 + .../example/sjkbf/mapper/UserAndServerMapper.java | 18 + .../java/com/example/sjkbf/mapper/UserMapper.java | 18 + .../sjkbf/mapper/UserServerProjectMapper.java | 18 + .../com/example/sjkbf/service/LogService.java | 16 + .../example/sjkbf/service/ProjectPerService.java | 86 + .../example/sjkbf/service/SaticScheduleTask.class | Bin 0 -> 3549 bytes .../sjkbf/service/UserAndServerService.java | 25 + .../example/sjkbf/service/UserOrderService.java | 174 ++ .../sjkbf/service/UserServerProjectService.java | 57 + .../com/example/sjkbf/service/UserService.java | 33 + .../sjkbf/service/impl/LogServiceImpl.java | 21 + .../sjkbf/service/impl/ProjectPerServiceImpl.java | 388 ++++ .../service/impl/UserAndServerServiceImpl.java | 58 + .../service/impl/UserDetailsServiceImpl.java | 51 + .../impl/UserServerProjectServiceImpl.java | 93 + .../sjkbf/service/impl/UserServiceImpl.java | 292 +++ .../com/example/sjkbf/util/JavaFXVideoPlayer.java | 340 ++++ .../java/com/example/sjkbf/util/JwtTokenUtil.java | 171 ++ .../src/main/java/com/example/sjkbf/util/MD5.java | 44 + .../main/java/com/example/sjkbf/util/RSATooL.java | 223 +++ .../sjkbf/util/SimpleNutZipCompatible.java | 250 +++ .../main/java/com/example/sjkbf/util/tool.java | 118 ++ .../src/main/resources/application.properties | 0 .../src/main/resources/application.yml | 78 + .../src/main/resources/mapper/LogMapper.xml | 5 + .../main/resources/mapper/ProjectPerMapper.xml | 5 + .../main/resources/mapper/UserAndServerMapper.xml | 5 + .../src/main/resources/mapper/UserMapper.xml | 5 + .../resources/mapper/UserServerProjectMapper.xml | 5 + 旧的java项目/src/main/resources/styles.css | 45 + .../com/example/sjkbf/SjkbfApplicationTests.java | 33 + 159 files changed, 12538 insertions(+) create mode 100644 .vscode/compile_commands.json create mode 100644 .xmake/windows/x64/cache/config create mode 100644 .xmake/windows/x64/cache/detect create mode 100644 .xmake/windows/x64/cache/history create mode 100644 .xmake/windows/x64/cache/option create mode 100644 .xmake/windows/x64/cache/package create mode 100644 .xmake/windows/x64/cache/project create mode 100644 .xmake/windows/x64/cache/references create mode 100644 .xmake/windows/x64/cache/repository create mode 100644 .xmake/windows/x64/cache/toolchain create mode 100644 .xmake/windows/x64/project.lock create mode 100644 .xmake/windows/x64/xmake.conf create mode 100644 README.md create mode 100644 build-debug-rebuild.log create mode 100644 build-debug.log create mode 100644 config/server.conf create mode 100644 config/server.conf.example create mode 100644 config/server.windows.conf create mode 100644 folder-alias.json create mode 100644 http-log-body-test.err create mode 100644 http-log-body-test.out create mode 100644 http-log-test.err create mode 100644 http-log-test.out create mode 100644 include/dps/app.hpp create mode 100644 include/dps/config.hpp create mode 100644 include/dps/fs_utils.hpp create mode 100644 include/dps/http.hpp create mode 100644 include/dps/json.hpp create mode 100644 include/dps/labels.hpp create mode 100644 packaging/systemd/dps-manager-server.service create mode 100644 private-folder-alias.json create mode 100644 server-run.err create mode 100644 server-run.out create mode 100644 server-start-check.err create mode 100644 server-start-check.out create mode 100644 server-unicode-check.err create mode 100644 server-unicode-check.out create mode 100644 src/app.cpp create mode 100644 src/config.cpp create mode 100644 src/fs_utils.cpp create mode 100644 src/http.cpp create mode 100644 src/json.cpp create mode 100644 src/labels.cpp create mode 100644 src/main.cpp create mode 100644 tests/config_sync_test.py create mode 100644 tests/crypto_smoke_test.cpp create mode 100644 tests/fs_utils_smoke_test.cpp create mode 100644 tests/get_info_level_contract_test.py create mode 100644 tests/json_smoke_test.cpp create mode 100644 tests/labels_smoke_test.cpp create mode 100644 tmp-verify.err create mode 100644 tmp-verify.out create mode 100644 xmake.lua create mode 100644 旧的java项目/.gitignore create mode 100644 旧的java项目/.mvn/wrapper/maven-wrapper.jar create mode 100644 旧的java项目/.mvn/wrapper/maven-wrapper.properties create mode 100644 旧的java项目/AGENTS.md create mode 100644 旧的java项目/itcast/client/ChatClient.java create mode 100644 旧的java项目/itcast/message/AbstractResponseMessage.java create mode 100644 旧的java项目/itcast/message/ChatRequestMessage.java create mode 100644 旧的java项目/itcast/message/ChatResponseMessage.java create mode 100644 旧的java项目/itcast/message/GroupChatRequestMessage.java create mode 100644 旧的java项目/itcast/message/GroupChatResponseMessage.java create mode 100644 旧的java项目/itcast/message/GroupCreateRequestMessage.java create mode 100644 旧的java项目/itcast/message/GroupCreateResponseMessage.java create mode 100644 旧的java项目/itcast/message/GroupJoinRequestMessage.java create mode 100644 旧的java项目/itcast/message/GroupJoinResponseMessage.java create mode 100644 旧的java项目/itcast/message/GroupMembersRequestMessage.java create mode 100644 旧的java项目/itcast/message/GroupMembersResponseMessage.java create mode 100644 旧的java项目/itcast/message/GroupQuitRequestMessage.java create mode 100644 旧的java项目/itcast/message/GroupQuitResponseMessage.java create mode 100644 旧的java项目/itcast/message/LoginRequestMessage.java create mode 100644 旧的java项目/itcast/message/LoginResponseMessage.java create mode 100644 旧的java项目/itcast/message/Message.java create mode 100644 旧的java项目/itcast/protocol/MessageCodec.java create mode 100644 旧的java项目/itcast/protocol/MessageCodecSharable.java create mode 100644 旧的java项目/itcast/protocol/ProcotolFrameDecoder.java create mode 100644 旧的java项目/itcast/server/ChatServer.java create mode 100644 旧的java项目/itcast/server/service/UserService.java create mode 100644 旧的java项目/itcast/server/service/UserServiceFactory.java create mode 100644 旧的java项目/itcast/server/service/UserServiceMemoryImpl.java create mode 100644 旧的java项目/itcast/server/session/Group.java create mode 100644 旧的java项目/itcast/server/session/GroupSession.java create mode 100644 旧的java项目/itcast/server/session/GroupSessionFactory.java create mode 100644 旧的java项目/itcast/server/session/GroupSessionMemoryImpl.java create mode 100644 旧的java项目/itcast/server/session/Session.java create mode 100644 旧的java项目/itcast/server/session/SessionFactory.java create mode 100644 旧的java项目/itcast/server/session/SessionMemoryImpl.java create mode 100644 旧的java项目/mvnw create mode 100644 旧的java项目/mvnw.cmd create mode 100644 旧的java项目/pom.xml create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/ApplicationRunnerImpl.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/CodeGenerator.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/SaticScheduleTask.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/SjkbfApplication.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/common/FileManage.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/config/Configuration/CurrentUserId.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/config/Configuration/WebMvcConfig.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/config/CorsConfig.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/config/JwtAuthenticationTokenFilter.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/config/MyMetaObjectHandler.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/config/RateLimitConfig.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/config/SecurityConfig.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/config/StartInit.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/config/VideoConfig.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/config/exception/CustomAuthenticationEntryPoint.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/config/exception/GlobalExceptionHandler.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/controller/AccountCargoController.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/controller/AuthController.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/controller/UserController.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/controller/UserServerProjectController.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/controller/VideoController.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/controller/ZfController.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/controller/verifyController.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/entity/AuthUser.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/entity/CommonResult.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/entity/DPSScriptJson.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/entity/DTO/LoginRequest.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/entity/DTO/LoginResponse.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/entity/Log.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/entity/ProjectPer.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/entity/User.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/entity/UserAndServer.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/entity/UserServerProject.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/entity/projectconfig/Project.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/entity/projectconfig/ProjectConfig.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/mapper/LogMapper.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/mapper/ProjectPerMapper.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/mapper/UserAndServerMapper.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/mapper/UserMapper.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/mapper/UserServerProjectMapper.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/LogService.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/ProjectPerService.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/SaticScheduleTask.class create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/UserAndServerService.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/UserOrderService.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/UserServerProjectService.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/UserService.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/impl/LogServiceImpl.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/impl/ProjectPerServiceImpl.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserAndServerServiceImpl.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserDetailsServiceImpl.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserServerProjectServiceImpl.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserServiceImpl.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/util/JavaFXVideoPlayer.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/util/JwtTokenUtil.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/util/MD5.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/util/RSATooL.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/util/SimpleNutZipCompatible.java create mode 100644 旧的java项目/src/main/java/com/example/sjkbf/util/tool.java create mode 100644 旧的java项目/src/main/resources/application.properties create mode 100644 旧的java项目/src/main/resources/application.yml create mode 100644 旧的java项目/src/main/resources/mapper/LogMapper.xml create mode 100644 旧的java项目/src/main/resources/mapper/ProjectPerMapper.xml create mode 100644 旧的java项目/src/main/resources/mapper/UserAndServerMapper.xml create mode 100644 旧的java项目/src/main/resources/mapper/UserMapper.xml create mode 100644 旧的java项目/src/main/resources/mapper/UserServerProjectMapper.xml create mode 100644 旧的java项目/src/main/resources/styles.css create mode 100644 旧的java项目/src/test/java/com/example/sjkbf/SjkbfApplicationTests.java diff --git a/.vscode/compile_commands.json b/.vscode/compile_commands.json new file mode 100644 index 0000000..f0ff664 --- /dev/null +++ b/.vscode/compile_commands.json @@ -0,0 +1,131 @@ +[ +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\crypto_smoke_test\\windows\\x64\\release\\tests\\crypto_smoke_test.cpp.obj", "tests\\crypto_smoke_test.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "tests\\crypto_smoke_test.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\crypto_smoke_test\\windows\\x64\\release\\src\\app.cpp.obj", "src\\app.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\app.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\crypto_smoke_test\\windows\\x64\\release\\src\\config.cpp.obj", "src\\config.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\config.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\crypto_smoke_test\\windows\\x64\\release\\src\\fs_utils.cpp.obj", "src\\fs_utils.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\fs_utils.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\crypto_smoke_test\\windows\\x64\\release\\src\\http.cpp.obj", "src\\http.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\http.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\crypto_smoke_test\\windows\\x64\\release\\src\\json.cpp.obj", "src\\json.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\json.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\crypto_smoke_test\\windows\\x64\\release\\src\\labels.cpp.obj", "src\\labels.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\labels.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c11", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\crypto_smoke_test\\windows\\x64\\release\\third_party\\libbcrypt\\crypt_blowfish\\wrapper.c.obj", "third_party\\libbcrypt\\crypt_blowfish\\wrapper.c", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "third_party\\libbcrypt\\crypt_blowfish\\wrapper.c" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c11", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\crypto_smoke_test\\windows\\x64\\release\\third_party\\libbcrypt\\crypt_blowfish\\crypt_blowfish.c.obj", "third_party\\libbcrypt\\crypt_blowfish\\crypt_blowfish.c", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "third_party\\libbcrypt\\crypt_blowfish\\crypt_blowfish.c" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c11", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\crypto_smoke_test\\windows\\x64\\release\\third_party\\libbcrypt\\crypt_blowfish\\crypt_gensalt.c.obj", "third_party\\libbcrypt\\crypt_blowfish\\crypt_gensalt.c", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "third_party\\libbcrypt\\crypt_blowfish\\crypt_gensalt.c" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/Fobuild\\.objs\\dps_manager_server\\windows\\x64\\release\\src\\app.cpp.obj", "src\\app.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\app.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/Fobuild\\.objs\\dps_manager_server\\windows\\x64\\release\\src\\config.cpp.obj", "src\\config.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\config.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/Fobuild\\.objs\\dps_manager_server\\windows\\x64\\release\\src\\fs_utils.cpp.obj", "src\\fs_utils.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\fs_utils.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/Fobuild\\.objs\\dps_manager_server\\windows\\x64\\release\\src\\http.cpp.obj", "src\\http.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\http.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/Fobuild\\.objs\\dps_manager_server\\windows\\x64\\release\\src\\json.cpp.obj", "src\\json.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\json.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/Fobuild\\.objs\\dps_manager_server\\windows\\x64\\release\\src\\labels.cpp.obj", "src\\labels.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\labels.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/Fobuild\\.objs\\dps_manager_server\\windows\\x64\\release\\src\\main.cpp.obj", "src\\main.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\main.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c11", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\dps_manager_server\\windows\\x64\\release\\third_party\\libbcrypt\\crypt_blowfish\\wrapper.c.obj", "third_party\\libbcrypt\\crypt_blowfish\\wrapper.c", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "third_party\\libbcrypt\\crypt_blowfish\\wrapper.c" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c11", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\dps_manager_server\\windows\\x64\\release\\third_party\\libbcrypt\\crypt_blowfish\\crypt_blowfish.c.obj", "third_party\\libbcrypt\\crypt_blowfish\\crypt_blowfish.c", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "third_party\\libbcrypt\\crypt_blowfish\\crypt_blowfish.c" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c11", "/Iinclude", "/Ithird_party\\libbcrypt\\crypt_blowfish", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\o\\openssl3\\3.6.1\\f6105560d35d4708bbf8084662ae6c0b\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.2\\1087d98a25354760941affad70a541d9\\include", "/IC:\\Users\\dongj\\AppData\\Local\\.xmake\\packages\\m\\mariadb-connector-c\\3.4.8\\15faf4d65a8b4e2b90b71d8039471456\\include", "/DNDEBUG", "/w", "/Fobuild\\.objs\\dps_manager_server\\windows\\x64\\release\\third_party\\libbcrypt\\crypt_blowfish\\crypt_gensalt.c.obj", "third_party\\libbcrypt\\crypt_blowfish\\crypt_gensalt.c", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "third_party\\libbcrypt\\crypt_blowfish\\crypt_gensalt.c" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/DNDEBUG", "/Fobuild\\.objs\\fs_utils_smoke_test\\windows\\x64\\release\\tests\\fs_utils_smoke_test.cpp.obj", "tests\\fs_utils_smoke_test.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "tests\\fs_utils_smoke_test.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/DNDEBUG", "/Fobuild\\.objs\\fs_utils_smoke_test\\windows\\x64\\release\\src\\fs_utils.cpp.obj", "src\\fs_utils.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\fs_utils.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/DNDEBUG", "/Fobuild\\.objs\\json_smoke_test\\windows\\x64\\release\\tests\\json_smoke_test.cpp.obj", "tests\\json_smoke_test.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "tests\\json_smoke_test.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/DNDEBUG", "/Fobuild\\.objs\\json_smoke_test\\windows\\x64\\release\\src\\json.cpp.obj", "src\\json.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\json.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/DNDEBUG", "/Fobuild\\.objs\\labels_smoke_test\\windows\\x64\\release\\tests\\labels_smoke_test.cpp.obj", "tests\\labels_smoke_test.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "tests\\labels_smoke_test.cpp" +}, +{ + "directory": "l:\\Qt_Project\\DPS_Manager\\服务端\\DpsManagerServer", + "arguments": ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe", "/c", "/nologo", "/MD", "/W3", "/WX", "/O2", "/std:c++17", "/Iinclude", "/DDPS_WINDOWS", "/DOPENSSL_SUPPRESS_DEPRECATED", "/EHsc", "/DNDEBUG", "/Fobuild\\.objs\\labels_smoke_test\\windows\\x64\\release\\src\\labels.cpp.obj", "src\\labels.cpp", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\ATLMFC\\include", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Auxiliary\\VS\\include", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt", "-imsvc", "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um", "-imsvc", "H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.34.31933\\include"], + "file": "src\\labels.cpp" +}] diff --git a/.xmake/windows/x64/cache/config b/.xmake/windows/x64/cache/config new file mode 100644 index 0000000..a8a36b4 --- /dev/null +++ b/.xmake/windows/x64/cache/config @@ -0,0 +1,9 @@ +{ + mtimes = { + ["xmake.lua"] = 1776155854 + }, + recheck = false, + options = { + clean = true + } +} \ No newline at end of file diff --git a/.xmake/windows/x64/cache/detect b/.xmake/windows/x64/cache/detect new file mode 100644 index 0000000..ba4dc14 --- /dev/null +++ b/.xmake/windows/x64/cache/detect @@ -0,0 +1,128 @@ +{ + find_program_fetch_package_system = { + nasm = [[H:\mingw64\bin\nasm.exe]], + cmake = [[H:\Cmake\bin\cmake]], + ninja = [[H:\mingw64\bin\ninja.exe]] + }, + find_program_fetch_package_xmake = { + nasm = false, + ninja = false, + ["jom.exe"] = false, + cmake = false, + perl = false + }, + ["lib.detect.has_flags"] = { + ["windows_x64_h:\\visual studio\\ide\\vc\\tools\\msvc\\14.43.34808\\bin\\hostx64\\x64\\cl.exe__cxx__-nologo_-O2"] = true, + ["windows_x64_h:\\visual studio\\ide\\vc\\tools\\msvc\\14.43.34808\\bin\\hostx64\\x64\\cl.exe__cc_cxflags_-nologo_cl_external_includedir"] = true, + ["windows_x64_h:\\visual studio\\ide\\vc\\tools\\msvc\\14.43.34808\\bin\\hostx64\\x64\\cl.exe__cc_cxflags_-nologo_-DNDEBUG"] = true, + ["windows_x64_h:\\visual studio\\ide\\vc\\tools\\msvc\\14.43.34808\\bin\\hostx64\\x64\\cl.exe__cxx_cxflags_-nologo_-DNDEBUG"] = true, + ["windows_x64_h:\\visual studio\\ide\\vc\\tools\\msvc\\14.43.34808\\bin\\hostx64\\x64\\cl.exe__cc_cxflags_-nologo_-std:c11"] = true, + ["windows_x64_h:\\visual studio\\ide\\vc\\tools\\msvc\\14.43.34808\\bin\\hostx64\\x64\\cl.exe__cc__-nologo_-O2"] = true, + ["windows_x64_h:\\visual studio\\ide\\vc\\tools\\msvc\\14.43.34808\\bin\\hostx64\\x64\\cl.exe__cxx_cxflags_-nologo_cl_external_includedir"] = true + }, + find_package_windows_x64_fetch_package_xmake = { + ["xmake::zlib_1087d98a25354760941affad70a541d9_release_v1.3.2_external"] = { + linkdirs = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\z\zlib\v1.3.2\1087d98a25354760941affad70a541d9\lib]] + }, + links = { + "zlib" + }, + sysincludedirs = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\z\zlib\v1.3.2\1087d98a25354760941affad70a541d9\include]] + }, + version = "v1.3.2", + license = "zlib", + libfiles = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\z\zlib\v1.3.2\1087d98a25354760941affad70a541d9\lib\zlib.lib]] + }, + static = true + }, + ["xmake::mariadb-connector-c_15faf4d65a8b4e2b90b71d8039471456_release_3.4.8_external"] = { + links = { + "mariadbclient" + }, + sysincludedirs = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\include]] + }, + version = "3.4.8", + libfiles = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\mariadbclient.lib]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\libmariadb.lib]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\libmariadb.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\auth_gssapi_client.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\caching_sha2_password.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\client_ed25519.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\dialog.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\mysql_clear_password.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\parsec.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\pvio_shmem.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\sha256_password.dll]] + }, + linkdirs = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb]] + }, + shared = true, + static = true, + license = "LGPL-2.1", + syslinks = { + "secur32", + "shlwapi", + "crypt32", + "bcrypt", + "advapi32", + "iphlpapi", + "ws2_32" + } + }, + ["xmake::openssl3_f6105560d35d4708bbf8084662ae6c0b_release_3.6.1_external"] = { + links = { + "libssl", + "libcrypto" + }, + sysincludedirs = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\o\openssl3\3.6.1\f6105560d35d4708bbf8084662ae6c0b\include]] + }, + version = "3.6.1", + libfiles = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\o\openssl3\3.6.1\f6105560d35d4708bbf8084662ae6c0b\lib\libssl.lib]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\o\openssl3\3.6.1\f6105560d35d4708bbf8084662ae6c0b\lib\libcrypto.lib]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\o\openssl3\3.6.1\f6105560d35d4708bbf8084662ae6c0b\lib\ossl-modules\legacy.dll]] + }, + linkdirs = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\o\openssl3\3.6.1\f6105560d35d4708bbf8084662ae6c0b\lib]] + }, + shared = true, + static = true, + license = "Apache-2.0", + syslinks = { + "ws2_32", + "user32", + "crypt32", + "advapi32" + } + } + }, + find_program = { + tar = [[C:\Windows\System32\tar.exe]], + nim = false, + ["cl.exe"] = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\cl.exe]], + git = [[C:\Program Files\Git\cmd\git.exe]], + ["H:\\Visual Studio\\IDE\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\cl.exe"] = [[h:\visual studio\ide\vc\tools\msvc\14.43.34808\bin\hostx64\x64\cl.exe]], + gzip = [[H:\ToonuOne\gzip.exe]] + }, + find_programver_fetch_package_system = { + ["H:\\mingw64\\bin\\nasm.exe"] = "2.16.03", + ["H:\\mingw64\\bin\\ninja.exe"] = "1.13.1", + ["H:\\Cmake\\bin\\cmake"] = "3.31.1" + }, + find_program_msvc_arch_x64_plat_windows_checktoolld = { + ["link.exe"] = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\link.exe]] + }, + find_program_msvc_arch_x64_plat_windows_checktoolcxx = { + ["cl.exe"] = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\cl.exe]] + }, + find_program_msvc_arch_x64_plat_windows_checktoolcc = { + ["cl.exe"] = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\cl.exe]] + } +} \ No newline at end of file diff --git a/.xmake/windows/x64/cache/history b/.xmake/windows/x64/cache/history new file mode 100644 index 0000000..28bc3b6 --- /dev/null +++ b/.xmake/windows/x64/cache/history @@ -0,0 +1,67 @@ +{ + cmdlines = { + "xmake check", + "xmake build fs_utils_smoke_test", + [[xmake l c:\Users\dongj\.trae-cn\extensions\tboox.xmake-vscode-2.5.4-universal\assets\update_intellisense.lua .vscode clangd]], + [[xmake l c:\Users\dongj\.trae-cn\extensions\tboox.xmake-vscode-2.5.4-universal\assets\explorer.lua]], + "xmake check", + "xmake build fs_utils_smoke_test", + "xmake build dps_manager_server", + "xmake build dps_manager_server", + "xmake build crypto_smoke_test", + "xmake run", + [[xmake l c:\Users\dongj\.trae-cn\extensions\tboox.xmake-vscode-2.5.4-universal\assets\update_intellisense.lua .vscode clangd]], + "xmake check", + "xmake run", + "xmake build labels_smoke_test", + [[xmake l c:\Users\dongj\.trae-cn\extensions\tboox.xmake-vscode-2.5.4-universal\assets\config.lua]], + [[xmake l c:\Users\dongj\.trae-cn\extensions\tboox.xmake-vscode-2.5.4-universal\assets\update_intellisense.lua .vscode clangd]], + [[xmake l c:\Users\dongj\.trae-cn\extensions\tboox.xmake-vscode-2.5.4-universal\assets\explorer.lua]], + "xmake check", + "xmake build labels_smoke_test", + "xmake build dps_manager_server", + "xmake ", + "xmake f -c", + "xmake run", + "xmake run", + "xmake run", + "xmake build dps_manager_server", + "xmake f -m debug", + "xmake build crypto_smoke_test", + "xmake run", + "xmake run", + "xmake run", + "xmake ", + "xmake f -p windows -a x64 -m debug", + "xmake run", + "xmake run", + "xmake run", + "xmake run", + "xmake g --proxy=http://127.0.0.1:7897", + "xmake ", + "xmake g --proxy=http://127.0.0.1:7897", + "xmake -r", + "xmake g --proxy=http://127.0.0.1:7897", + "xmake ", + "xmake ", + "xmake ", + "xmake ", + "xmake ", + "xmake ", + "xmake run", + "xmake run", + [[xmake lua "C:\\Program Files\\xmake\\modules\\private\\utils\\statistics.lua"]], + [[xmake lua "C:\\Program Files\\xmake\\actions\\build\\cleaner.lua"]], + "xmake g --proxy=http://127.0.0.1:7897", + "xmake build", + "xmake f -m debug", + "xmake build -v", + "xmake g --proxy=http://127.0.0.1:7897", + "xmake build -v", + "xmake g --proxy=http://127.0.0.1:7897", + "xmake build", + "xmake run", + [[xmake l c:\Users\dongj\.trae-cn\extensions\tboox.xmake-vscode-2.5.4-universal\assets\explorer.lua]], + [[xmake l c:\Users\dongj\.trae-cn\extensions\tboox.xmake-vscode-2.5.4-universal\assets\config.lua]] + } +} \ No newline at end of file diff --git a/.xmake/windows/x64/cache/option b/.xmake/windows/x64/cache/option new file mode 100644 index 0000000..6f31cf5 --- /dev/null +++ b/.xmake/windows/x64/cache/option @@ -0,0 +1 @@ +{ } \ No newline at end of file diff --git a/.xmake/windows/x64/cache/package b/.xmake/windows/x64/cache/package new file mode 100644 index 0000000..c66abb9 --- /dev/null +++ b/.xmake/windows/x64/cache/package @@ -0,0 +1,87 @@ +{ + openssl3 = { + version = "3.6.1", + envs = { + PATH = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\n\nasm\2.16.03\a21bbd6e91e3451bb2fbb65664f020c8\bin]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\s\strawberry-perl\5.32.0+1\b64caa2665494fc5b5ca88386f62ce65\bin]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\s\strawberry-perl\5.32.0+1\b64caa2665494fc5b5ca88386f62ce65\perl\bin]] + } + }, + links = { + "libssl", + "libcrypto" + }, + sysincludedirs = [[C:\Users\dongj\AppData\Local\.xmake\packages\o\openssl3\3.6.1\f6105560d35d4708bbf8084662ae6c0b\include]], + __requirestr = "openssl3", + license = "Apache-2.0", + installdir = [[C:\Users\dongj\AppData\Local\.xmake\packages\o\openssl3\3.6.1\f6105560d35d4708bbf8084662ae6c0b]], + linkdirs = [[C:\Users\dongj\AppData\Local\.xmake\packages\o\openssl3\3.6.1\f6105560d35d4708bbf8084662ae6c0b\lib]], + syslinks = { + "ws2_32", + "user32", + "crypt32", + "advapi32" + }, + libfiles = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\o\openssl3\3.6.1\f6105560d35d4708bbf8084662ae6c0b\lib\libssl.lib]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\o\openssl3\3.6.1\f6105560d35d4708bbf8084662ae6c0b\lib\libcrypto.lib]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\o\openssl3\3.6.1\f6105560d35d4708bbf8084662ae6c0b\lib\ossl-modules\legacy.dll]] + }, + static = true, + __enabled = true, + shared = true + }, + ["mariadb-connector-c"] = { + version = "3.4.8", + envs = { + PATH = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\n\ninja\v1.13.1\fd5a0995dca64146a4f4c4dbff879aad\bin]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\c\cmake\4.2.3\674adf9609f646bc9f678b5e946b080a\bin]] + } + }, + links = "mariadbclient", + sysincludedirs = [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\include]], + __requirestr = "mariadb-connector-c", + license = "LGPL-2.1", + installdir = [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456]], + linkdirs = [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb]], + syslinks = { + "secur32", + "shlwapi", + "crypt32", + "bcrypt", + "advapi32", + "iphlpapi", + "ws2_32" + }, + libfiles = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\mariadbclient.lib]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\libmariadb.lib]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\libmariadb.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\auth_gssapi_client.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\caching_sha2_password.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\client_ed25519.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\dialog.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\mysql_clear_password.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\parsec.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\pvio_shmem.dll]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456\lib\mariadb\plugin\sha256_password.dll]] + }, + static = true, + __enabled = true, + shared = true + }, + zlib = { + version = "v1.3.2", + links = "zlib", + sysincludedirs = [[C:\Users\dongj\AppData\Local\.xmake\packages\z\zlib\v1.3.2\1087d98a25354760941affad70a541d9\include]], + __requirestr = "zlib", + installdir = [[C:\Users\dongj\AppData\Local\.xmake\packages\z\zlib\v1.3.2\1087d98a25354760941affad70a541d9]], + license = "zlib", + linkdirs = [[C:\Users\dongj\AppData\Local\.xmake\packages\z\zlib\v1.3.2\1087d98a25354760941affad70a541d9\lib]], + static = true, + __enabled = true, + libfiles = [[C:\Users\dongj\AppData\Local\.xmake\packages\z\zlib\v1.3.2\1087d98a25354760941affad70a541d9\lib\zlib.lib]] + } +} \ No newline at end of file diff --git a/.xmake/windows/x64/cache/project b/.xmake/windows/x64/cache/project new file mode 100644 index 0000000..6f31cf5 --- /dev/null +++ b/.xmake/windows/x64/cache/project @@ -0,0 +1 @@ +{ } \ No newline at end of file diff --git a/.xmake/windows/x64/cache/references b/.xmake/windows/x64/cache/references new file mode 100644 index 0000000..7cd2fd6 --- /dev/null +++ b/.xmake/windows/x64/cache/references @@ -0,0 +1,9 @@ +{ + packages = { + [[C:\Users\dongj\AppData\Local\.xmake\packages\s\strawberry-perl\5.32.0+1\b64caa2665494fc5b5ca88386f62ce65]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\j\jom\1.1.4\9a79be4085074ec7bc62e782058794c3]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\o\openssl3\3.6.1\f6105560d35d4708bbf8084662ae6c0b]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\z\zlib\v1.3.2\1087d98a25354760941affad70a541d9]], + [[C:\Users\dongj\AppData\Local\.xmake\packages\m\mariadb-connector-c\3.4.8\15faf4d65a8b4e2b90b71d8039471456]] + } +} \ No newline at end of file diff --git a/.xmake/windows/x64/cache/repository b/.xmake/windows/x64/cache/repository new file mode 100644 index 0000000..76b3713 --- /dev/null +++ b/.xmake/windows/x64/cache/repository @@ -0,0 +1,13 @@ +{ + artifacts_urls = { + "https://gitee.com/xmake-mirror/build-artifacts.git", + "https://gitlab.com/xmake-mirror/build-artifacts.git", + "https://github.com/xmake-mirror/build-artifacts.git" + }, + mainurls = { + "https://gitee.com/tboox/xmake-repo.git", + "https://gitcode.com/xmake-io/xmake-repo.git", + "https://gitlab.com/tboox/xmake-repo.git", + "https://github.com/xmake-io/xmake-repo.git" + } +} \ No newline at end of file diff --git a/.xmake/windows/x64/cache/toolchain b/.xmake/windows/x64/cache/toolchain new file mode 100644 index 0000000..c5f0bc3 --- /dev/null +++ b/.xmake/windows/x64/cache/toolchain @@ -0,0 +1,219 @@ +{ + tool_target_labels_smoke_test_windows_x64_cxx = { + toolname = "cl", + toolchain_info = { + name = "msvc", + plat = "windows", + arch = "x64", + cachekey = "msvc_arch_x64_plat_windows" + }, + program = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\cl.exe]] + }, + nim_arch_x64_plat_windows = { + __global = true, + __checked = false, + arch = "x64", + plat = "windows" + }, + tool_target_dps_manager_server_windows_x64_cc = { + toolname = "cl", + toolchain_info = { + name = "msvc", + plat = "windows", + arch = "x64", + cachekey = "msvc_arch_x64_plat_windows" + }, + program = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\cl.exe]] + }, + tool_target_json_smoke_test_windows_x64_ld = { + toolname = "link", + toolchain_info = { + name = "msvc", + plat = "windows", + arch = "x64", + cachekey = "msvc_arch_x64_plat_windows" + }, + program = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\link.exe]] + }, + clang_arch_x64_plat_windows = { + __global = true, + arch = "x64", + plat = "windows" + }, + tool_target_json_smoke_test_windows_x64_cxx = { + toolname = "cl", + toolchain_info = { + name = "msvc", + plat = "windows", + arch = "x64", + cachekey = "msvc_arch_x64_plat_windows" + }, + program = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\cl.exe]] + }, + fpc_arch_x64_plat_windows = { + __global = true, + __checked = true, + arch = "x64", + plat = "windows" + }, + gfortran_arch_x64_plat_windows = { + __global = true, + __checked = true, + arch = "x64", + plat = "windows" + }, + nasm_arch_x64_plat_windows = { + __global = true, + __checked = true, + arch = "x64", + plat = "windows" + }, + tool_target_crypto_smoke_test_windows_x64_cxx = { + toolname = "cl", + toolchain_info = { + name = "msvc", + plat = "windows", + arch = "x64", + cachekey = "msvc_arch_x64_plat_windows" + }, + program = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\cl.exe]] + }, + go_arch_x64_plat_windows = { + __global = true, + __checked = true, + arch = "x64", + plat = "windows" + }, + tool_target_fs_utils_smoke_test_windows_x64_cxx = { + toolname = "cl", + toolchain_info = { + name = "msvc", + plat = "windows", + arch = "x64", + cachekey = "msvc_arch_x64_plat_windows" + }, + program = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\cl.exe]] + }, + tool_target_fs_utils_smoke_test_windows_x64_ld = { + toolname = "link", + toolchain_info = { + name = "msvc", + plat = "windows", + arch = "x64", + cachekey = "msvc_arch_x64_plat_windows" + }, + program = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\link.exe]] + }, + zig_arch_x64_plat_windows = { + __global = true, + arch = "x64", + plat = "windows" + }, + msvc_arch_x64_plat_windows = { + vs_toolset = "14.43.34808", + plat = "windows", + vs = "2022", + vs_sdkver = "10.0.22621.0", + vcarchs = { + "arm", + "arm64", + "arm64ec", + "x64", + "x86" + }, + __checked = "2022", + vcvars = { + WindowsSdkVerBinPath = [[C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\]], + VCToolsRedistDir = [[H:\Visual Studio\IDE\VC\Redist\MSVC\14.42.34433\]], + UCRTVersion = "10.0.22621.0", + VS170COMNTOOLS = [[H:\Visual Studio\IDE\Common7\Tools\]], + WindowsSdkDir = [[C:\Program Files (x86)\Windows Kits\10\]], + WindowsLibPath = [[C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.22621.0;C:\Program Files (x86)\Windows Kits\10\References\10.0.22621.0]], + VCToolsVersion = "14.43.34808", + VSCMD_ARG_app_plat = "Desktop", + WindowsSDKVersion = "10.0.22621.0", + PATH = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64;H:\Visual Studio\IDE\Common7\IDE\VC\VCPackages;H:\Visual Studio\IDE\Common7\IDE\CommonExtensions\Microsoft\TestWindow;H:\Visual Studio\IDE\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer;H:\Visual Studio\IDE\MSBuild\Current\bin\Roslyn;C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64\;C:\Program Files (x86)\HTML Help Workshop;H:\Visual Studio\IDE\Common7\IDE\CommonExtensions\Microsoft\FSharp\Tools;H:\Visual Studio\IDE\Team Tools\DiagnosticsHub\Collector;C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\\x64;C:\Program Files (x86)\Windows Kits\10\bin\\x64;H:\Visual Studio\IDE\\MSBuild\Current\Bin\amd64;C:\Windows\Microsoft.NET\Framework64\v4.0.30319;H:\Visual Studio\IDE\Common7\IDE\;H:\Visual Studio\IDE\Common7\Tools\;C:\Program Files\Git\mingw64\bin;C:\Program Files\Git\usr\bin;C:\Users\dongj\bin;H:\mingw64\bin;C:\Program Files\Python313\Scripts;C:\Program Files\Python313;H:\Cmake\bin;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.3\bin;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.3\libnvvp;C:\Program Files\Microsoft\jdk-11.0.12.7-hotspot\bin;C:\Program Files\Common Files\Oracle\Java\javapath;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0;C:\WINDOWS\System32\OpenSSH;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\dotnet;C:\Program Files\Git\cmd;H:\Bandizip;H:\Visual Studio\IDE\MSBuild\Microsoft\VisualStudio\NodeJs;H:\Visual Studio\IDE\Msbuild\Microsoft\VisualStudio\NodeJs;H:\WechatSdk\dll;H:\OpenSSL-Win64\include;C:\ProgramData\chocolatey\bin;C:\Program Files\NVIDIA Corporation\Nsight Compute 2023.3.0;C:\Program Files\cURL\bin;H:\Visual Studio\IDE\VC\Tools\MSVC\14.34.31933\bin\Hostx86\x86;H:\Visual Studio\Tool_And_SDK\Python39_64;C:\Program Files\NVIDIA Corporation\NVIDIA app\NvDLISR;H:\C++_Lib\Vcpkg\vcpkg;H:\flutter\bin;C:\Program Files (x86)\Windows Kits\10\Windows Performance Toolkit;L:\Qt\QtApp\5.15.2\msvc2015_64;L:\Qt\QtApp\5.15.2\msvc2015_64\bin;L:\Qt\QtApp\Tools\CMake_64\bin;H:\ToonuOne;L:\SSH\WezTerm;H:\cursor\resources\app\bin;C:\Program Files\xmake;H:\mingw64\bin;H:\Trae CN\bin;H:\Cmake\bin;C:\Users\dongj\scoop\shims;L:\Cocos\cocos2d-x\templates;L:\Cocos\cocos2d-x\tools\cocos2d-console\bin;cocos2d-x-4.0\templates;C:\Users\dongj\AppData\Local\Microsoft\WindowsApps;C:\Users\dongj\.dotnet\tools;C:\Users\dongj\AppData\Local\Programs\Microsoft VS Code\bin;flutter;flutter\bin;Visual Studio\IDE\MSBuild\Microsoft\VisualStudio\NodeJs;C:\Users\dongj\AppData\Roaming\npm;L:\CLion 2024.3.4\bin;OpenSSL-Win64\include;C:\tools\dart-sdk\bin;C:\Users\dongj\AppData\Local\Pub\Cache\bin;JDK8\JAVA;C++_Lib\Vcpkg\vcpkg;C:\Users\dongj\.bun\bin;C:\Users\dongj\AppData\Local\Programs\Ollama;C:\Users\dongj\AppData\Local\Microsoft\WindowsApps;L:\Switch\devkitPro\tools\bin;H:\Visual Studio\IDE\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin;H:\Visual Studio\IDE\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja;H:\Visual Studio\IDE\Common7\IDE\VC\Linux\bin\ConnectionManagerExe;H:\Visual Studio\IDE\VC\vcpkg]], + WindowsSdkBinPath = [[C:\Program Files (x86)\Windows Kits\10\bin\]], + VSInstallDir = [[H:\Visual Studio\IDE\]], + VCInstallDir = [[H:\Visual Studio\IDE\VC\]], + VSCMD_VER = "17.13.5", + UniversalCRTSdkDir = [[C:\Program Files (x86)\Windows Kits\10\]], + VSCMD_ARG_TGT_ARCH = "x64", + VisualStudioVersion = "17.0", + VSCMD_ARG_HOST_ARCH = "x64", + VCIDEInstallDir = [[H:\Visual Studio\IDE\Common7\IDE\VC\]], + ExtensionSdkDir = [[C:\Program Files (x86)\Microsoft SDKs\Windows Kits\10\ExtensionSDKs]], + LIB = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\ATLMFC\lib\x64;H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\lib\x64;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\lib\um\x64;C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\ucrt\x64;C:\Program Files (x86)\Windows Kits\10\\lib\10.0.22621.0\\um\x64]], + DevEnvdir = [[H:\Visual Studio\IDE\Common7\IDE\]], + VCToolsInstallDir = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\]], + LIBPATH = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\ATLMFC\lib\x64;H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\lib\x64;H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\lib\x86\store\references;C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.22621.0;C:\Program Files (x86)\Windows Kits\10\References\10.0.22621.0;C:\Windows\Microsoft.NET\Framework64\v4.0.30319]], + INCLUDE = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\include;H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\ATLMFC\include;H:\Visual Studio\IDE\VC\Auxiliary\VS\include;C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt;C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\um;C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\shared;C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\winrt;C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\cppwinrt;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\include\um;H:\Visual Studio\IDE\VC\Tools\MSVC\14.34.31933\include]] + }, + arch = "x64", + __global = true + }, + yasm_arch_x64_plat_windows = { + __global = true, + __checked = true, + arch = "x64", + plat = "windows" + }, + tool_target_labels_smoke_test_windows_x64_ld = { + toolname = "link", + toolchain_info = { + name = "msvc", + plat = "windows", + arch = "x64", + cachekey = "msvc_arch_x64_plat_windows" + }, + program = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\link.exe]] + }, + rust_arch_x64_plat_windows = { + __global = true, + __checked = true, + arch = "x64", + plat = "windows" + }, + tool_target_crypto_smoke_test_windows_x64_ld = { + toolname = "link", + toolchain_info = { + name = "msvc", + plat = "windows", + arch = "x64", + cachekey = "msvc_arch_x64_plat_windows" + }, + program = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\link.exe]] + }, + tool_target_dps_manager_server_windows_x64_cxx = { + toolname = "cl", + toolchain_info = { + name = "msvc", + plat = "windows", + arch = "x64", + cachekey = "msvc_arch_x64_plat_windows" + }, + program = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\cl.exe]] + }, + cuda_arch_x64_plat_windows = { + __global = true, + __checked = true, + arch = "x64", + plat = "windows" + }, + swift_arch_x64_plat_windows = { + __global = true, + __checked = true, + arch = "x64", + plat = "windows" + }, + tool_target_dps_manager_server_windows_x64_ld = { + toolname = "link", + toolchain_info = { + name = "msvc", + plat = "windows", + arch = "x64", + cachekey = "msvc_arch_x64_plat_windows" + }, + program = [[H:\Visual Studio\IDE\VC\Tools\MSVC\14.43.34808\bin\HostX64\x64\link.exe]] + } +} \ No newline at end of file diff --git a/.xmake/windows/x64/project.lock b/.xmake/windows/x64/project.lock new file mode 100644 index 0000000..e69de29 diff --git a/.xmake/windows/x64/xmake.conf b/.xmake/windows/x64/xmake.conf new file mode 100644 index 0000000..64540c9 --- /dev/null +++ b/.xmake/windows/x64/xmake.conf @@ -0,0 +1,38 @@ +{ + __toolchains_windows_x64 = { + "msvc", + "yasm", + "nasm", + "cuda", + "rust", + "swift", + "go", + "gfortran", + "fpc" + }, + __toolchains_windows_x64_host = { + "msvc", + "yasm", + "nasm", + "cuda", + "rust", + "swift", + "go", + "gfortran", + "fpc" + }, + arch = "x64", + builddir = "build", + ccache = true, + clean = true, + host = "windows", + kind = "static", + mode = "release", + ndk_stdcxx = true, + network = "public", + plat = "windows", + proxy = "http://127.0.0.1:7897", + proxy_pac = "pac.lua", + theme = "default", + vs = "2022" +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..008e13f --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +# DPS Manager Server + +This directory contains the current C++17 service implementation. The legacy Java project is kept only as a behavior reference under `旧的java项目/`. + +## Build targets + +The server now uses one codebase with the same runtime behavior on: + +- Windows: `MSVC + xmake` +- Linux: `gcc/clang + xmake` + +`mingw` is no longer the primary target for this service. + +## Dependencies + +Xmake resolves these shared dependencies for both platforms: + +- `openssl3` +- `zlib` +- `mariadb-connector-c` + +Platform system links: + +- Windows: `ws2_32` +- Linux: `pthread` + +## Database TLS + +- `database.ssl_mode=disable`: disable TLS for local Windows testing +- `database.ssl_mode=preferred`: allow TLS without strict certificate validation +- `database.ssl_mode=required`: require TLS but skip certificate validation +- `database.ssl_mode=verify_ca`: require TLS and verify the server certificate chain +- `database.ssl_mode=verify_identity`: same strict verification path as `verify_ca` in the current Connector/C integration +- `database.ssl_ca`: optional CA certificate file path for strict verification modes +- `database.plugin_dir`: optional MariaDB auth plugin directory override; Windows builds also copy connector plugins to `mariadb-plugin` beside the executable + +## Build + +### Windows local testing + +```powershell +xmake f -p windows -a x64 -m debug +xmake +Copy-Item config/server.windows.conf config/server.conf +``` + +### Linux deployment build + +```bash +xmake f -p linux -a x86_64 -m release +xmake +cp config/server.conf.example config/server.conf +``` + +## Run + +Windows: + +```powershell +./build/windows/x64/debug/dps_manager_server.exe --config config/server.conf +``` + +Linux: + +```bash +./build/linux/x86_64/release/dps_manager_server --config config/server.conf +``` + +## Notes + +- Password hashing, JWT signing, RSA operations, and gzip payload generation all use the same OpenSSL/zlib-based implementation on Windows and Linux. +- The server fails fast on startup if the database connection cannot be established. +- Windows local testing can set `database.ssl_mode=disable` when the remote database presents a certificate chain that Windows does not trust yet. +- Proxy headers are ignored by default; enable `server.trust_proxy=true` only when the service runs behind a trusted reverse proxy. +- `config/server.conf.example` is the Linux-oriented template, and `config/server.windows.conf` is the Windows local test template. diff --git a/build-debug-rebuild.log b/build-debug-rebuild.log new file mode 100644 index 0000000000000000000000000000000000000000..613597a8ea3ee91efe444e447ada9f764ff860be GIT binary patch literal 20636 zcmeHPNpD*>5T0ua^cOS%9t;$55Ja{X%S($OX`D7j5+|*l1&9hktL?;=3`vfi06n+Y z9{Zzu?7fHnh928~-%vE4-jnoXZFXBiki^GFauyC}hRg8pzdn;UvMfi!|Ey%>3rXYY zjRg3t%IEUEZ0I|d2{e^Ksv=GN9?O9o;@gltNl8m;c&bTB+V~BmBl~#QlByI@vV>Ge zK1e}pl%{siKb1Oax6wuc=@#lA<5NQ_h0+%5PvsL?kVn9=fX_>< zae}gkfWL#5PXPS@t?})aY|6uKxd-~ci#JV_ZR^uh)Oac8OK5{6LN6(eedLEFL@vhLP zzv47r4)q^_-AB-Tpm@Fqos{+zC@3h+YoM_z>!7ZLycMOKbdwM3;6NJrb$m)FMQ)T( zhPkYBmFYeLc9e=1C@1b^NIf-L1D_gF9i&^DXJmo8fTKSR`bcdZR93--6{INvIn>JF ze_eZgRXIS7kyS47Ift_OzC78~5%y5Nf_#6YBj8kjzXlFvjB#q%Dr%|qDsi3B&L;mq>x;MLHr6Rtb?w2gG5R&eX|1q& z>vR>Z)BIK!`|DR{Mf$&=kIpx1zT9Wx{*~AK+)3h^v7eXtd5NEw9An>doIS;>y}LAj z&EHSU#`S-{QuDC_`&f!U7Qmk`n2!DPzQ6bVxwaD3uyO4lEioU>&(jo? zbg%in=JzMdrn%`vC>D$HP1meadMh? z)UbwE>z?mtv;^0-5AfeUufd&OLb`=^Qu;AbE-n_P3FzVdV4Joz@1iE*+IWuu5g$t< z;lRj{g$be?c0mcFaH4Sz89kxTllY8FDFVAZc6Kv33(mNNb(|~biGD`4r1i`?^H_&x z`d5*gQ_fX1y@pz;b31M@?u}zv7|NXrW`Dj12dyQK+JkBIPZs@A({B#31r_v58F^Kt zmNnL(hTl5gucL4C-TWNNFg{`29?SxQi3p>FZ$Msu*!^Ss$xi`d3K&1pI3JgrDoKnI z2^g>RZt>NxKmYRW^mpZ3cYYHBpSI?vu@R=gFI}1(v5a_Rr;ToVchs>U?@j4M28fPM~Qx zXQGGXa+z0AuPd6rk5*_c7%l9|JB)hEhvR}>X-4V2@3zrf{s8R~0!MH!1V+7O;QjQL zzoWh(IT?FHfnfb#GuKMi)}zPoS&zH-1fzvFZ!G%l<6agn(Oreh!XvuI zby;{sXI%5fV_H$_!y`?@XWU^bT1RtD9kSK2{K+=Zl!hGo}{R@+`0^MUlx_Kwg}70`JP2iv1pOh1`x@h#*x zFq3Cod0Q>9#qy%9k`o7Nk9mg!p*SjfX&}EFSU)K$)VJy*bb6q#VBSbR58_aiBFAq= z>D${oEF5|ahAOUXn3kXDEf$XfgK!wX&wC5hN7@1n5ViSewuuGD1YM6L0Bum8^^qILOTo@aBb;9+jkLM35G+r%~GeH|yXk0YYSGq zhSa!xo+cHmkmw{TjLj~dYxi;S9>p8romZJYWB00PlwsrE=v+IRM&*(lH5a!8jF?O6 zy~!-WXnnQ}dz^#^>7-}ThE}~eZ=A;G9bVkiv3_P)Kd%4}XOBkpX0DCp$6OoeGrAsi zSoil|hI>w?4m~j5L}zZdVe^~mNAm<_UsG&1EOH-MF0Q* literal 0 HcmV?d00001 diff --git a/build-debug.log b/build-debug.log new file mode 100644 index 0000000000000000000000000000000000000000..b3062375ee5f9a307ff10825bef8338e978e3a70 GIT binary patch literal 762 zcma)4(Q3j#5S(YBU(lC(@vVr)LZST>TO=kamgbCjC(@s<&TJCAnhGH#cbA>rnVa47 z?*k^-;D|5ODB(~b=etG4eL&K=?Q~ePh7A&{#Ut=j<3BV=ZXARx8&<5 zS!zb)WzQr3Jl%_2Q@o@9^5hqKgw>iG!0`qpecC7iXHK`E|K943!YZ|s#8;ZP+gC4w#x?Sbaor6e4@_4TU4e~ OPOJ0Jc;Vl^mbfnyqj(Ge literal 0 HcmV?d00001 diff --git a/config/server.conf b/config/server.conf new file mode 100644 index 0000000..7d186fc --- /dev/null +++ b/config/server.conf @@ -0,0 +1,34 @@ +# Windows local test configuration +# Fill in the database settings and adjust all paths to match your machine. +server.host=0.0.0.0 +server.port=65170 + +jwt.secret=rjyusrghdfghj2345ryu123asdfvbyukuirtwjhfsd +jwt.expiration_seconds=7200 + +logging.level=debug +logging.http_summary=true +logging.http_dump=true +logging.http_max_body=2048 + +# Keep these disabled for the first Windows test pass. +features.video_enabled=false +features.payment_enabled=false + +# MySQL test database +database.host=192.168.200.179 +database.port=3306 +database.name=rindro +database.user=root +database.password=Djq5231520! +database.ssl_mode=disable +database.ssl_ca= +database.plugin_dir= + +# Main legacy-compatible Windows test paths +paths.configfile=H:\\DpsTest\\rindro\\Script\\FileConfig.json +paths.file_root=H:\\DpsTest\\rindro\\ +paths.file_map=H:\\DpsTest\\rindro\\file.json +paths.script_root=H:\\DpsTest\\rindro\\ClentScript\\ +paths.dps_root=H:\\DpsTest\\DP_S\\ +paths.client_user_script_root=H:\\DpsTest\\rindro\\ClentUserScript\\ diff --git a/config/server.conf.example b/config/server.conf.example new file mode 100644 index 0000000..3008dfe --- /dev/null +++ b/config/server.conf.example @@ -0,0 +1,33 @@ +# Linux deployment example +# Copy this file to config/server.conf and adjust the database credentials and paths. +server.host=0.0.0.0 +server.port=8651 +server.trust_proxy=false +server.proxy_header=x-forwarded-for + +jwt.secret=change-me +jwt.expiration_seconds=7200 + +logging.level=info +logging.http_summary=true +logging.http_dump=false +logging.http_max_body=2048 + +features.video_enabled=false +features.payment_enabled=false + +database.host=127.0.0.1 +database.port=3306 +database.name=rindro +database.user=root +database.password=change-me +database.ssl_mode=preferred +database.ssl_ca= +database.plugin_dir= + +paths.configfile=/root/rindro/Script/FileConfig.json +paths.file_root=/root/rindro/ +paths.file_map=/root/rindro/file.json +paths.script_root=/root/rindro/ClentScript/ +paths.dps_root=/home/DP_S/ +paths.client_user_script_root=/root/rindro/ClentUserScript/ diff --git a/config/server.windows.conf b/config/server.windows.conf new file mode 100644 index 0000000..3529503 --- /dev/null +++ b/config/server.windows.conf @@ -0,0 +1,33 @@ +# Windows local test example for MSVC builds +# Copy this file to config/server.conf and adjust the database credentials and paths. +server.host=0.0.0.0 +server.port=8651 +server.trust_proxy=false +server.proxy_header=x-forwarded-for + +jwt.secret=change-me +jwt.expiration_seconds=7200 + +logging.level=debug +logging.http_summary=true +logging.http_dump=true +logging.http_max_body=2048 + +features.video_enabled=false +features.payment_enabled=false + +database.host=127.0.0.1 +database.port=3306 +database.name=rindro +database.user=root +database.password=change-me +database.ssl_mode=disable +database.ssl_ca= +database.plugin_dir= + +paths.configfile=D:\\DpsTest\\rindro\\Script\\FileConfig.json +paths.file_root=D:\\DpsTest\\rindro\\ +paths.file_map=D:\\DpsTest\\rindro\\file.json +paths.script_root=D:\\DpsTest\\rindro\\ClentScript\\ +paths.dps_root=D:\\DpsTest\\DP_S\\ +paths.client_user_script_root=D:\\DpsTest\\rindro\\ClentUserScript\\ diff --git a/folder-alias.json b/folder-alias.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/folder-alias.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/http-log-body-test.err b/http-log-body-test.err new file mode 100644 index 0000000..ea22c77 --- /dev/null +++ b/http-log-body-test.err @@ -0,0 +1 @@ +Listening on 0.0.0.0:65170 diff --git a/http-log-body-test.out b/http-log-body-test.out new file mode 100644 index 0000000..e69de29 diff --git a/http-log-test.err b/http-log-test.err new file mode 100644 index 0000000..9afb667 --- /dev/null +++ b/http-log-test.err @@ -0,0 +1,3 @@ +Listening on 0.0.0.0:65170 +[2026-04-14 03:31:55] REQ ip=127.0.0.1 method=GET path=/rindro/getversion query=- body_bytes=0 content_type=- auth=absent +[2026-04-14 03:31:55] RESP ip=127.0.0.1 method=GET path=/rindro/getversion status=200 content_type=text/plain; charset=utf-8 body_bytes=146 binary=no cost_ms=0 diff --git a/http-log-test.out b/http-log-test.out new file mode 100644 index 0000000..e69de29 diff --git a/include/dps/app.hpp b/include/dps/app.hpp new file mode 100644 index 0000000..f70e0bb --- /dev/null +++ b/include/dps/app.hpp @@ -0,0 +1,227 @@ +#pragma once + +#include "dps/config.hpp" +#include "dps/http.hpp" +#include "dps/json.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace dps { + +struct User { + int id = 0; + std::string username; + std::string password; + int gold = 0; + int level = 1; + std::string qq; + std::string name; + std::string img; + std::string client_key; + std::string server_key; +}; + +struct UserAndServer { + std::string user_name; + std::string server_ip; + std::string account; + std::string password; + std::string port; +}; + +struct UserServerProject { + std::string server_ip; + std::string project_name; + std::string user_name; + std::string expiration_date; + int open = 1; +}; + +struct ProjectDefinition { + std::string name; + std::vector script_paths; + std::map info; + int price = 0; + bool is_private = false; + std::string img_path; +}; + +class LegacyCrypto { +public: + explicit LegacyCrypto(std::string jwt_secret); + + std::string issue_jwt(const std::string &username, long long expiration_seconds) const; + std::optional validate_jwt(const std::string &token) const; + + std::string hash_password(const std::string &password) const; + bool verify_password(const std::string &password, const std::string &encoded) const; + + std::string rsa_private_encrypt_tool(const std::string &plain) const; + std::string rsa_private_encrypt_script(const std::string &plain) const; + std::string gzip_base64(const std::string &plain) const; + std::vector makecode(std::vector bytes, const std::vector &key) const; + std::string xor_encrypt(const std::string &plain, const std::string &key) const; + +private: + std::string base64_url_encode(const std::string &input) const; + std::string base64_url_decode(const std::string &input) const; + std::string hmac_sha256(const std::string &message) const; + std::string sha256_hex(const std::string &message) const; + std::string bcrypt_hash(const std::string &password) const; + std::string rsa_private_encrypt_der_b64(const std::string &der_b64, const std::string &plain) const; + + std::string jwt_secret_; +}; + +class Database { +public: + explicit Database(const DatabaseConfig &config); + ~Database(); + + bool connect(); + bool is_connected() const; + std::string last_error() const; + std::string escape(const std::string &value) const; + bool execute(const std::string &sql); + std::vector> query(const std::string &sql); + long long last_insert_id() const; + +private: + struct Impl; + std::unique_ptr impl_; +}; + +class ProjectCatalog { +public: + explicit ProjectCatalog(const PathConfig &paths); + + void reload(); + std::optional get_project(const std::string &name, const std::string &version = "") const; + Json public_project_info() const; + Json all_project_info() const; + int project_price(const std::string &name) const; + std::string project_img_path(const std::string &name) const; + std::vector base_script_contents(const std::string &version, bool cs_mode) const; + std::vector project_script_contents(const std::string &project_name, const std::string &version) const; + std::vector custom_client_scripts(const std::string &ip, const std::string &version) const; + std::vector> dps_service_scripts(const std::string &ip) const; + std::vector> dps_service_scripts_new(const std::string &ip, const std::string &version) const; + std::string newest_version() const; + long long base_update_time(const std::string &version) const; + +private: + struct VersionData { + std::string name; + std::map projects; + std::vector base_scripts; + std::vector cs_base_scripts; + std::map> project_scripts; + std::map> custom_client_scripts; + Json dps_manifest; + }; + + const VersionData *find_version(const std::string &requested) const; + std::vector read_script_list(const std::string &root, const std::vector &items) const; + std::vector> read_named_scripts(const std::string &root, const std::vector &items) const; + void load_client_versions(); + void load_custom_client_scripts(VersionData &data); + Json parse_json_file(const std::string &path) const; + + PathConfig paths_; + std::map versions_; + std::string newest_version_; +}; + +class Application { +public: + explicit Application(AppConfig config); + int run(); + +private: + using Row = std::map; + + void register_routes(); + Response handle_login(const Request &request); + Response handle_register(const Request &request); + Response handle_change_password(const Request &request); + Response handle_get_info(const Request &request); + Response handle_add_server(const Request &request); + Response handle_delete_server(const Request &request); + Response handle_get_server_list(const Request &request); + Response handle_get_server(const Request &request); + Response handle_get_project_info_list(const Request &request); + Response handle_get_user_project_list(const Request &request); + Response handle_get_server_project_list(const Request &request); + Response handle_buy(const Request &request); + Response handle_add_gold(const Request &request); + Response handle_transfer(const Request &request); + Response handle_set_key(const Request &request); + Response handle_set_project_open(const Request &request); + + Response handle_rindro_download(const Request &request); + Response handle_static_download(const std::string &disk_path, const std::string &download_name) const; + Response handle_get_image(const Request &request, bool thumbnail); + Response handle_get_mp42(const Request &request); + Response handle_get_version(const Request &request); + + Response handle_dps_version(const std::string &relative_file) const; + Response handle_dps_list(const std::string &relative_dir, const std::string &metadata_file) const; + Response handle_dps_download(const Request &request, const std::string &base_dir) const; + + Response handle_script_client(const Request &request); + Response handle_script_client2(const Request &request); + Response handle_script_getzdy(const Request &request); + Response handle_script_getpro(const Request &request); + Response handle_script_getservice(const Request &request); + Response handle_script_base_update_time(const Request &request); + Response handle_script_base_sut(const Request &request); + Response handle_script_getservice_new(const Request &request); + + Response handle_video_placeholder(const Request &request); + Response handle_payment_placeholder(const Request &request); + + std::optional find_user_by_username(const std::string &username); + bool username_exists(const std::string &username); + bool create_user(const std::string &username, const std::string &password, const std::string &qq); + bool update_password(const std::string &username, const std::string &qq, const std::string &password); + bool update_user_row(const User &user); + std::vector servers_for_user(const std::string &username); + std::optional find_server(const std::string &username, const std::string &ip); + bool upsert_server(const UserAndServer &server); + bool delete_server(const std::string &username, const std::string &ip); + std::vector projects_for_ip(const std::string &ip, bool require_open = false); + std::vector projects_for_user(const std::string &username); + std::optional find_project_binding(const std::string &ip, const std::string &project_name); + bool add_or_extend_project(const std::string &ip, const std::string &project_name, int days, const std::string &username); + bool update_project_open(const std::string &ip, const std::string &project_name, int open_value); + bool insert_log(const std::string &username, int type, const std::string &content); + + Response json_response(int status, const Json &body) const; + Response common_success(const Json &data, const std::string &message = "操作成功", int status = 200) const; + Response common_failed(int code, const std::string &message, int http_status = 200) const; + Response file_response(const std::filesystem::path &path, const std::string &download_name = "") const; + std::string request_ip(const Request &request) const; + std::vector random_key() const; + std::string format_date_now() const; + std::string add_days(const std::string &iso_date, int days) const; + std::string make_random_name() const; + Json parse_body_or_empty(const Request &request) const; + std::optional current_user(const Request &request) const; + + AppConfig config_; + Router router_; + LegacyCrypto crypto_; + Database database_; + ProjectCatalog catalog_; + mutable std::mutex transfer_mutex_; + mutable std::mutex script_mutex_; + std::map ip_and_mac_; +}; + +} // namespace dps diff --git a/include/dps/config.hpp b/include/dps/config.hpp new file mode 100644 index 0000000..ad885c5 --- /dev/null +++ b/include/dps/config.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include + +namespace dps { + +struct ServerConfig { + std::string host = "0.0.0.0"; + int port = 8651; + bool trust_proxy = false; + std::string proxy_header = "x-forwarded-for"; +}; + +struct JwtConfig { + std::string secret = "change-me"; + long long expiration_seconds = 7200; +}; + +struct LoggingConfig { + std::string level = "info"; + bool http_summary = true; + bool http_dump = false; + int http_max_body = 2048; +}; + +struct FeatureConfig { + bool video_enabled = false; + bool payment_enabled = false; +}; + +struct DatabaseConfig { + std::string host = "127.0.0.1"; + int port = 3306; + std::string name = "rindro"; + std::string user = "root"; + std::string password; + std::string ssl_mode = "preferred"; + std::string ssl_ca; + std::string plugin_dir; +}; + +struct PathConfig { + std::string configfile = "/root/rindro/Script/FileConfig.json"; + std::string file_root = "/root/rindro/"; + std::string file_map = "/root/rindro/file.json"; + std::string script_root = "/root/rindro/ClentScript/"; + std::string dps_root = "/home/DP_S/"; + std::string client_user_script_root = "/root/rindro/ClentUserScript/"; +}; + +struct AppConfig { + ServerConfig server; + JwtConfig jwt; + LoggingConfig logging; + FeatureConfig features; + DatabaseConfig database; + PathConfig paths; +}; + +AppConfig load_config(const std::string &path); + +} // namespace dps diff --git a/include/dps/fs_utils.hpp b/include/dps/fs_utils.hpp new file mode 100644 index 0000000..b06c513 --- /dev/null +++ b/include/dps/fs_utils.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +namespace dps { + +std::filesystem::path path_from_utf8(const std::string &value); +std::string path_to_utf8_string(const std::filesystem::path &path); +std::string read_text_file(const std::filesystem::path &path); +std::vector read_binary_file(const std::filesystem::path &path); + +} // namespace dps diff --git a/include/dps/http.hpp b/include/dps/http.hpp new file mode 100644 index 0000000..0cd9002 --- /dev/null +++ b/include/dps/http.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include "dps/config.hpp" + +#include +#include +#include +#include +#include + +namespace dps { + +struct Request { + std::string method; + std::string raw_target; + std::string path; + std::string body; + std::string remote_addr; + std::map headers; + std::map query; + std::map path_params; + std::optional current_user; + + std::string header(const std::string &name) const; + std::string query_value(const std::string &name) const; + std::string path_param(const std::string &name) const; +}; + +struct Response { + int status = 200; + std::string content_type = "application/json; charset=utf-8"; + std::string body; + std::vector binary_body; + std::map headers; + + bool has_binary() const; +}; + +using Handler = std::function; + +class Router { +public: + void add(const std::string &method, const std::string &pattern, bool requires_auth, Handler handler); + Response dispatch(Request request) const; + +private: + struct Route { + std::string method; + std::string pattern; + bool requires_auth = false; + Handler handler; + }; + + std::vector routes_; +}; + +class HttpServer { +public: + HttpServer(std::string host, int port, LoggingConfig logging, const Router &router); + int run() const; + +private: + std::string host_; + int port_; + LoggingConfig logging_; + const Router &router_; +}; + +std::string url_decode(const std::string &value); +std::map parse_query_string(const std::string &query); +std::string guess_content_type(const std::string &path); + +} // namespace dps diff --git a/include/dps/json.hpp b/include/dps/json.hpp new file mode 100644 index 0000000..dca1003 --- /dev/null +++ b/include/dps/json.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace dps { + +class Json { +public: + using array_t = std::vector; + using object_t = std::map; + using value_t = std::variant; + + Json(); + Json(std::nullptr_t); + Json(bool value); + Json(int value); + Json(long long value); + Json(double value); + Json(const char *value); + Json(const std::string &value); + Json(std::string &&value); + Json(const array_t &value); + Json(array_t &&value); + Json(const object_t &value); + Json(object_t &&value); + + static Json parse(const std::string &text); + static Json object(); + static Json array(); + + bool is_null() const; + bool is_bool() const; + bool is_number() const; + bool is_string() const; + bool is_array() const; + bool is_object() const; + + bool as_bool() const; + double as_number() const; + int as_int() const; + long long as_int64() const; + const std::string &as_string() const; + const array_t &as_array() const; + const object_t &as_object() const; + array_t &as_array(); + object_t &as_object(); + + bool contains(const std::string &key) const; + const Json &at(const std::string &key) const; + Json &operator[](const std::string &key); + const Json &operator[](const std::string &key) const; + + const Json &at(std::size_t index) const; + Json &at(std::size_t index); + void push_back(const Json &value); + std::size_t size() const; + + std::string dump() const; + std::string to_string_or(const std::string &fallback) const; + int to_int_or(int fallback) const; + bool to_bool_or(bool fallback) const; + +private: + value_t value_; +}; + +} // namespace dps diff --git a/include/dps/labels.hpp b/include/dps/labels.hpp new file mode 100644 index 0000000..d340247 --- /dev/null +++ b/include/dps/labels.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace dps { + +std::string membership_label(int level); + +} // namespace dps diff --git a/packaging/systemd/dps-manager-server.service b/packaging/systemd/dps-manager-server.service new file mode 100644 index 0000000..5a95d64 --- /dev/null +++ b/packaging/systemd/dps-manager-server.service @@ -0,0 +1,15 @@ +[Unit] +Description=DPS Manager Server +After=network.target mysql.service + +[Service] +Type=simple +WorkingDirectory=/opt/dps-manager-server +ExecStart=/opt/dps-manager-server/bin/dps_manager_server --config /opt/dps-manager-server/config/server.conf +Restart=always +RestartSec=3 +User=root +Group=root + +[Install] +WantedBy=multi-user.target diff --git a/private-folder-alias.json b/private-folder-alias.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/private-folder-alias.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/server-run.err b/server-run.err new file mode 100644 index 0000000..a662ad2 --- /dev/null +++ b/server-run.err @@ -0,0 +1,4 @@ +Listening on 0.0.0.0:65170 +[2026-04-14 03:58:00] REQ ip=127.0.0.1 method=POST path=/login query=- body_bytes=17 content_type=application/json;charset=UTF-8 auth=absent +[2026-04-14 03:58:00] REQ_BODY username:lenheart +[2026-04-14 03:58:00] RESP ip=127.0.0.1 method=POST path=/login status=401 content_type=application/json; charset=utf-8 body_bytes=61 binary=no cost_ms=0 diff --git a/server-run.out b/server-run.out new file mode 100644 index 0000000..e69de29 diff --git a/server-start-check.err b/server-start-check.err new file mode 100644 index 0000000..ea22c77 --- /dev/null +++ b/server-start-check.err @@ -0,0 +1 @@ +Listening on 0.0.0.0:65170 diff --git a/server-start-check.out b/server-start-check.out new file mode 100644 index 0000000..e69de29 diff --git a/server-unicode-check.err b/server-unicode-check.err new file mode 100644 index 0000000..cf1e730 --- /dev/null +++ b/server-unicode-check.err @@ -0,0 +1,6 @@ +Listening on 0.0.0.0:65170 +[2026-04-14 16:14:00] REQ ip=127.0.0.1 method=GET path=/dps/download3/3_%E5%A4%9F%E9%92%9F:img.png query=- body_bytes=0 content_type=- auth=absent +[2026-04-14 16:14:00] RESP ip=127.0.0.1 method=GET path=/dps/download3/3_%E5%A4%9F%E9%92%9F:img.png status=200 content_type=image/png body_bytes=173653 binary=yes cost_ms=1 +[2026-04-14 16:14:01] REQ ip=127.0.0.1 method=GET path=/dps/download3/..:..:windows/win.ini query=- body_bytes=0 content_type=- auth=absent +[2026-04-14 16:14:01] RESP ip=127.0.0.1 method=GET path=/dps/download3/..:..:windows/win.ini status=404 content_type=application/json; charset=utf-8 body_bytes=46 binary=no cost_ms=0 +[2026-04-14 16:14:01] RESP_BODY {"code":404,"message":"not found","data":null} diff --git a/server-unicode-check.out b/server-unicode-check.out new file mode 100644 index 0000000..e69de29 diff --git a/src/app.cpp b/src/app.cpp new file mode 100644 index 0000000..2d68232 --- /dev/null +++ b/src/app.cpp @@ -0,0 +1,1586 @@ +#include "dps/app.hpp" +#include "dps/fs_utils.hpp" +#include "dps/labels.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#else +#include +#endif + +extern "C" { +#include "ow-crypt.h" +} + +namespace fs = std::filesystem; + +namespace dps { + +namespace { + +const char *kToolPrivateKey = + "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMiYuNW4K1rST7ZW" + "YpWX6nEziXi5JveLPhDCLj0VZ/5/4dzBWrmoL/IcFZuHOBJtYHm965713kKC9gtw" + "2EyVgkqmXLT3105jEUqzNizfThc6C2ZL6vMmzUZlooxNyaOC5mWthPZtwlqQihYW" + "T2nW/wKp8fpTouXihQOCPjqdRoVFAgMBAAECgYEAg+1dcsHjUaIL5uG9iKEXAUhh" + "21H6PMgJE8CB5I6VjJ3Sj/tijcP9dH/f/h/aUtQ4pRixRCRz/s5ev3uB5ixWOVOz" + "Uhga/NwjURlkg1Katg/14tvd8iZsAzI7KyXGdUefC8+Rulc8SBx+BqYIZ1FTLJkg" + "obisps6esaaNZekvS0ECQQDxPnfWuNfwfpYFK038AhEFqBiOBMsMrB/QX+O0sQ/Z" + "31gWkNuu3Z+cz6PG0FEK0O8/jsXQ1/gVAreADptJAMzpAkEA1N3GFV1LuYYqkp3a" + "y4DnmdI/LgrBUAjWSvTSDKfB8Ik2Y8I2zDS20EsEQMvQYtLxMOMHIEqeAVzmnZcS" + "dH4L/QJAQM/elFJuuU7Y8SSUO/s2JYXmqukAwDPSDEJmw5m6P3dwjAd47b7e7dsf" + "Df/Tdgtx62ppHNWY8dQcBoxmDbPoWQJBANBQBBROS9fQA4Od9Usn5/5xcSDWp51y" + "OHv8ID8AQNvq+44ets+aWrl2YEAk9NZxRlWoJwYj34LH7muUNxxHaeUCQQCjtLPU" + "din4XjlMMRSTW/EOgX4JgZ+tQaAtB32wQocPDDeHqkDusUdlEpnDPbmkE9Sbxf1p" + "0mSxXgnIuT99niBd"; + +const char *kScriptPrivateKey = + "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANp/KnuAZq609lnc" + "jciVgt/xDLDEaKC+DSuSZc9o/fgLqweL/8Yu5iPAzoBQlO1t+jSkI2YuoFsKKK7O" + "QFcSoz4Z5Lb7vOG7XJ4Re/6L2LJIU9tjJ3LznW5WKnuiqxs0ziIZilLkRE0P1di6" + "FIZQUjmn2KQ2y/P7PuviJPat3y+vAgMBAAECgYEAmn64h0MvV/FVEA1Ho8E0HSzQ" + "kF0Qrjg0D88gdhwDGFUHxfpUDMo0qKs4WLqh05CkDnzRGvt1H2p7gb8M94SSL5SX" + "mCR2ZLiCQJy5tZpQNRUt9P67upLcDcKJ6YPujcuEdlo0pzKGGMZk8Uvh05S9lnwl" + "yWJeEk4G7q5Aalz8vIECQQDwBVJH60Gd2St/GS4dCtXwHTO9HJZRaWEkaXPgVbXI" + "vqv+jtD4HpzDCpgv/kUK0fnb4V/9/LM1SHOedVMfmh0fAkEA6QsChdHiU1lHfL1b" + "Z3Uvxvi/Zk8i0ryzBaYudrzNmb7iJcssJPPty3KTjihgvw2W36GV+2GlQBEjOf2B" + "2WgLcQJBAO66XDxsIbd+aWThBpycSm2one1aoagXyCcPO9HFbilcfHWUVwRybjkQ" + "MI6LuOAqOPoaD//vd89nYJga2bJ09sECQFmXRPoDTVozqXr4JSqp75szyAliBQY1" + "SzGxyI0XWodvzesvp6HxMQsU2ks9lKv+YnFI4qsIyAnQTNWfcwsMp9ECQDba0LJV" + "xI0SIznsVtBb7T5Ir9ppLKx1QTNcytkjHxmQqWGy+57ojjQ6GV3z4fSwNGjIhsOT" + "DFgz0gEcRr13bRk="; + +std::string trim(const std::string &value) { + std::size_t left = 0; + while (left < value.size() && std::isspace(static_cast(value[left]))) { + ++left; + } + std::size_t right = value.size(); + while (right > left && std::isspace(static_cast(value[right - 1]))) { + --right; + } + return value.substr(left, right - left); +} + +std::string join_path(const std::string &a, const std::string &b) { + if (a.empty()) return b; + if (b.empty()) return a; + if (a.back() == '/' || a.back() == '\\') return a + b; + return a + "/" + b; +} + +std::string format_now() { + const auto now = std::chrono::system_clock::now(); + const std::time_t time = std::chrono::system_clock::to_time_t(now); + std::tm local{}; +#ifdef _WIN32 + localtime_s(&local, &time); +#else + localtime_r(&time, &local); +#endif + std::ostringstream out; + out << std::put_time(&local, "%Y-%m-%d %H:%M:%S"); + return out.str(); +} + +std::optional parse_time(const std::string &value) { + std::tm tm{}; + std::istringstream full(value); + full >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); + if (!full.fail()) return tm; + std::istringstream short_value(value); + short_value >> std::get_time(&tm, "%Y-%m-%d"); + if (!short_value.fail()) return tm; + return std::nullopt; +} + +std::string add_days_impl(const std::string &value, int days) { + auto parsed = parse_time(value); + std::time_t now_time = std::time(nullptr); + std::tm tm{}; + if (parsed.has_value()) { + tm = *parsed; + } else { +#ifdef _WIN32 + localtime_s(&tm, &now_time); +#else + localtime_r(&now_time, &tm); +#endif + } + std::time_t base = std::mktime(&tm); + base += static_cast(days) * 24 * 60 * 60; +#ifdef _WIN32 + localtime_s(&tm, &base); +#else + localtime_r(&base, &tm); +#endif + std::ostringstream out; + out << std::put_time(&tm, "%Y-%m-%d %H:%M:%S"); + return out.str(); +} + +std::string to_json_bytes(const std::vector &bytes) { + std::ostringstream out; + for (std::size_t i = 0; i < bytes.size(); ++i) { + if (i != 0) out << ","; + out << static_cast(static_cast(bytes[i])); + } + return out.str(); +} + +std::vector split_csv_config(const std::string &path) { + std::vector result; + std::ifstream input(path); + std::string line; + while (std::getline(input, line)) { + line = trim(line); + if (!line.empty()) result.push_back(line); + } + return result; +} + +long long latest_mtime_ms(const fs::path &root) { + if (!fs::exists(root)) return 0; + long long latest = 0; + for (const auto &entry : fs::recursive_directory_iterator(root)) { + if (!entry.is_regular_file()) continue; + const auto file_time = entry.last_write_time(); + const auto system_time = std::chrono::time_point_cast( + file_time - fs::file_time_type::clock::now() + std::chrono::system_clock::now()); + const auto time = std::chrono::duration_cast(system_time.time_since_epoch()).count(); + if (time > latest) latest = time; + } + return latest; +} + +Json map_to_json_object(const std::map &values) { + Json result = Json::object(); + for (const auto &entry : values) result[entry.first] = entry.second; + return result; +} + +std::string to_lower_copy(std::string value) { + std::transform(value.begin(), value.end(), value.begin(), [](unsigned char ch) { + return static_cast(std::tolower(ch)); + }); + return value; +} + +bool path_prefix_matches(const fs::path &root, const fs::path &target) { + auto root_it = root.begin(); + auto target_it = target.begin(); + for (; root_it != root.end(); ++root_it, ++target_it) { + if (target_it == target.end()) return false; + if (to_lower_copy(path_to_utf8_string(*root_it)) != to_lower_copy(path_to_utf8_string(*target_it))) { + return false; + } + } + return true; +} + +std::string current_executable_dir() { +#ifdef _WIN32 + std::array buffer{}; + const DWORD size = GetModuleFileNameA(nullptr, buffer.data(), static_cast(buffer.size())); + if (size == 0 || size == buffer.size()) return {}; + return fs::path(std::string(buffer.data(), size)).parent_path().string(); +#else + std::array buffer{}; + const ssize_t size = readlink("/proc/self/exe", buffer.data(), buffer.size()); + if (size <= 0 || static_cast(size) >= buffer.size()) return {}; + return fs::path(std::string(buffer.data(), static_cast(size))).parent_path().string(); +#endif +} + +constexpr int kBcryptBufferSize = 64; + +std::string base64_encode(const std::string &input, bool url_safe, bool pad_output) { + static const char *kBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::string output; + int value = 0; + int bits = -6; + for (const unsigned char ch : input) { + value = (value << 8) + ch; + bits += 8; + while (bits >= 0) { + output.push_back(kBase64[(value >> bits) & 0x3F]); + bits -= 6; + } + } + if (bits > -6) output.push_back(kBase64[((value << 8) >> (bits + 8)) & 0x3F]); + if (pad_output) { + while (output.size() % 4 != 0) output.push_back('='); + } + if (url_safe) { + std::replace(output.begin(), output.end(), '+', '-'); + std::replace(output.begin(), output.end(), '/', '_'); + if (!pad_output) { + while (!output.empty() && output.back() == '=') output.pop_back(); + } + } + return output; +} + +std::optional base64_decode(std::string input, bool url_safe) { + if (url_safe) { + std::replace(input.begin(), input.end(), '-', '+'); + std::replace(input.begin(), input.end(), '_', '/'); + } + while (input.size() % 4 != 0) input.push_back('='); + + static const std::string kBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::array lookup{}; + lookup.fill(-1); + for (std::size_t i = 0; i < kBase64.size(); ++i) lookup[static_cast(kBase64[i])] = static_cast(i); + + std::string output; + int value = 0; + int bits = -8; + for (const unsigned char ch : input) { + if (ch == '=') break; + if (lookup[ch] == -1) return std::nullopt; + value = (value << 6) + lookup[ch]; + bits += 6; + if (bits >= 0) { + output.push_back(static_cast((value >> bits) & 0xFF)); + bits -= 8; + } + } + return output; +} + +bool fill_random_bytes(std::array &buffer) { + return RAND_bytes(buffer.data(), static_cast(buffer.size())) == 1; +} + +std::string bcrypt_hash_impl(const std::string &password) { + std::array raw{}; + if (!fill_random_bytes(raw)) return ""; + + std::array salt{}; + if (crypt_gensalt_rn("$2b$", 12, reinterpret_cast(raw.data()), + static_cast(raw.size()), salt.data(), + static_cast(salt.size())) == nullptr) { + return ""; + } + + std::array hash{}; + if (crypt_rn(password.c_str(), salt.data(), hash.data(), static_cast(hash.size())) == nullptr) return ""; + return std::string(hash.data()); +} + +bool bcrypt_verify_impl(const std::string &password, const std::string &encoded) { + std::array hash{}; + char *result = crypt_rn(password.c_str(), encoded.c_str(), hash.data(), static_cast(hash.size())); + return result != nullptr && encoded == result; +} + +} // namespace + +LegacyCrypto::LegacyCrypto(std::string jwt_secret) : jwt_secret_(std::move(jwt_secret)) {} + +std::string LegacyCrypto::base64_url_encode(const std::string &input) const { + return base64_encode(input, true, false); +} + +std::string LegacyCrypto::base64_url_decode(const std::string &input) const { + return base64_decode(input, true).value_or(""); +} + +std::string LegacyCrypto::hmac_sha256(const std::string &message) const { + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int digest_size = 0; + HMAC(EVP_sha256(), jwt_secret_.data(), static_cast(jwt_secret_.size()), + reinterpret_cast(message.data()), message.size(), + digest, &digest_size); + return std::string(reinterpret_cast(digest), digest_size); +} + +std::string LegacyCrypto::sha256_hex(const std::string &message) const { + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast(message.data()), message.size(), digest); + std::ostringstream out; + out << std::hex << std::setfill('0'); + for (unsigned char byte : digest) out << std::setw(2) << static_cast(byte); + return out.str(); +} + +std::string LegacyCrypto::bcrypt_hash(const std::string &password) const { + return bcrypt_hash_impl(password); +} + +std::string LegacyCrypto::rsa_private_encrypt_der_b64(const std::string &der_b64, const std::string &plain) const { + const std::string der = base64_decode(der_b64, false).value_or(""); + if (der.empty()) return ""; + const unsigned char *ptr = reinterpret_cast(der.data()); + RSA *rsa = d2i_RSAPrivateKey(nullptr, &ptr, static_cast(der.size())); + if (rsa == nullptr) return ""; + std::vector encrypted(RSA_size(rsa)); + const int size = RSA_private_encrypt(static_cast(plain.size()), reinterpret_cast(plain.data()), encrypted.data(), rsa, RSA_PKCS1_PADDING); + RSA_free(rsa); + if (size <= 0) return ""; + std::string raw(reinterpret_cast(encrypted.data()), static_cast(size)); + return base64_encode(raw, false, true); +} + +std::string LegacyCrypto::issue_jwt(const std::string &username, long long expiration_seconds) const { + Json header = Json::object(); + header["alg"] = "HS256"; + header["typ"] = "JWT"; + Json payload = Json::object(); + const auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + payload["sub"] = username; + payload["username"] = username; + Json roles = Json::array(); + roles.push_back("ROLE_ALL"); + payload["authorities"] = roles; + payload["iat"] = static_cast(now); + payload["exp"] = static_cast(now + expiration_seconds); + const std::string token_body = base64_url_encode(header.dump()) + "." + base64_url_encode(payload.dump()); + return token_body + "." + base64_url_encode(hmac_sha256(token_body)); +} + +std::optional LegacyCrypto::validate_jwt(const std::string &token) const { + const std::size_t first = token.find('.'); + const std::size_t second = token.find('.', first == std::string::npos ? first : first + 1); + if (first == std::string::npos || second == std::string::npos) return std::nullopt; + const std::string body = token.substr(0, second); + if (base64_url_encode(hmac_sha256(body)) != token.substr(second + 1)) return std::nullopt; + const Json payload = Json::parse(base64_url_decode(token.substr(first + 1, second - first - 1))); + const long long exp = payload["exp"].to_int_or(0); + const auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + if (now >= exp) return std::nullopt; + return payload["sub"].to_string_or(""); +} + +std::string LegacyCrypto::hash_password(const std::string &password) const { return bcrypt_hash(password); } + +bool LegacyCrypto::verify_password(const std::string &password, const std::string &encoded) const { + return encoded.rfind("$2", 0) == 0 && bcrypt_verify_impl(password, encoded); +} + +std::string LegacyCrypto::rsa_private_encrypt_tool(const std::string &plain) const { return rsa_private_encrypt_der_b64(kToolPrivateKey, plain); } +std::string LegacyCrypto::rsa_private_encrypt_script(const std::string &plain) const { return rsa_private_encrypt_der_b64(kScriptPrivateKey, plain); } + +std::string LegacyCrypto::gzip_base64(const std::string &plain) const { + z_stream stream{}; + if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK) return ""; + stream.next_in = reinterpret_cast(const_cast(plain.data())); + stream.avail_in = static_cast(plain.size()); + std::string compressed; + std::array buffer{}; + int ret = Z_OK; + while (ret == Z_OK) { + stream.next_out = reinterpret_cast(buffer.data()); + stream.avail_out = static_cast(buffer.size()); + ret = deflate(&stream, Z_FINISH); + compressed.append(buffer.data(), buffer.size() - stream.avail_out); + } + deflateEnd(&stream); + if (ret != Z_STREAM_END) return ""; + return base64_encode(compressed, false, true); +} + +std::vector LegacyCrypto::makecode(std::vector bytes, const std::vector &key) const { + for (std::size_t i = 0; i < bytes.size(); ++i) { + const int k1 = key[i % key.size()]; + const int k2 = key[(i + 18) % key.size()]; + const int value = static_cast(bytes[i]); + bytes[i] = static_cast((((value + k1) ^ k2) ^ k1) + 1); + } + return bytes; +} + +std::string LegacyCrypto::xor_encrypt(const std::string &plain, const std::string &key) const { + if (key.empty()) return plain; + std::string output = plain; + for (std::size_t i = 0; i < plain.size(); ++i) output[i] = static_cast(plain[i] ^ key[i % key.size()]); + return output; +} + +struct Database::Impl { + MYSQL *connection = nullptr; + DatabaseConfig config; + std::string error; + mutable std::recursive_mutex mutex; +}; + +bool set_mysql_bool_option(MYSQL *connection, enum mysql_option option, bool value, std::string *error) { + my_bool flag = value ? 1 : 0; + if (mysql_options(connection, option, &flag) == 0) return true; + if (error != nullptr) *error = mysql_error(connection); + return false; +} + +bool set_mysql_string_option(MYSQL *connection, enum mysql_option option, const std::string &value, std::string *error) { + if (value.empty()) return true; + if (mysql_options(connection, option, value.c_str()) == 0) return true; + if (error != nullptr) *error = mysql_error(connection); + return false; +} + +bool configure_database_tls(MYSQL *connection, const DatabaseConfig &config, std::string *error) { + const std::string mode = to_lower_copy(config.ssl_mode.empty() ? "preferred" : config.ssl_mode); + if (mode == "disable") { + if (!set_mysql_bool_option(connection, MYSQL_OPT_SSL_ENFORCE, false, error)) return false; + if (!set_mysql_bool_option(connection, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, false, error)) return false; + return true; + } + if (mode == "preferred") { + if (!set_mysql_bool_option(connection, MYSQL_OPT_SSL_ENFORCE, false, error)) return false; + if (!set_mysql_bool_option(connection, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, false, error)) return false; + return true; + } + if (mode == "required") { + if (!set_mysql_bool_option(connection, MYSQL_OPT_SSL_ENFORCE, true, error)) return false; + if (!set_mysql_bool_option(connection, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, false, error)) return false; + return true; + } + if (mode == "verify_ca" || mode == "verify_identity") { + if (!set_mysql_bool_option(connection, MYSQL_OPT_SSL_ENFORCE, true, error)) return false; + if (!set_mysql_bool_option(connection, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, true, error)) return false; + if (!set_mysql_string_option(connection, MYSQL_OPT_SSL_CA, config.ssl_ca, error)) return false; + return true; + } + if (error != nullptr) *error = "unsupported database.ssl_mode: " + config.ssl_mode; + return false; +} + +bool configure_database_plugins(MYSQL *connection, const DatabaseConfig &config, std::string *error) { + std::string plugin_dir = config.plugin_dir; + if (plugin_dir.empty()) { + const std::string exe_dir = current_executable_dir(); + if (!exe_dir.empty()) { + const fs::path bundled_dir = fs::path(exe_dir) / "mariadb-plugin"; + if (fs::exists(bundled_dir) && fs::is_directory(bundled_dir)) { + plugin_dir = bundled_dir.string(); + } + } + } + if (plugin_dir.empty()) return true; + return set_mysql_string_option(connection, MYSQL_PLUGIN_DIR, plugin_dir, error); +} + +Database::Database(const DatabaseConfig &config) : impl_(new Impl{}) { impl_->config = config; } + +Database::~Database() { + std::lock_guard lock(impl_->mutex); + if (impl_->connection != nullptr) { + mysql_close(impl_->connection); + impl_->connection = nullptr; + } +} + +bool Database::connect() { + std::lock_guard lock(impl_->mutex); + if (impl_->connection != nullptr) return true; + + impl_->connection = mysql_init(nullptr); + if (impl_->connection == nullptr) { + impl_->error = "mysql_init failed"; + return false; + } + if (!configure_database_tls(impl_->connection, impl_->config, &impl_->error)) { + mysql_close(impl_->connection); + impl_->connection = nullptr; + return false; + } + if (!configure_database_plugins(impl_->connection, impl_->config, &impl_->error)) { + mysql_close(impl_->connection); + impl_->connection = nullptr; + return false; + } + if (mysql_real_connect(impl_->connection, impl_->config.host.c_str(), + impl_->config.user.c_str(), impl_->config.password.c_str(), + impl_->config.name.c_str(), impl_->config.port, nullptr, 0) == nullptr) { + impl_->error = mysql_error(impl_->connection); + mysql_close(impl_->connection); + impl_->connection = nullptr; + return false; + } + if (mysql_set_character_set(impl_->connection, "utf8mb4") != 0) { + impl_->error = mysql_error(impl_->connection); + mysql_close(impl_->connection); + impl_->connection = nullptr; + return false; + } + return true; +} + +bool Database::is_connected() const { + std::lock_guard lock(impl_->mutex); + return impl_->connection != nullptr; +} + +std::string Database::last_error() const { + std::lock_guard lock(impl_->mutex); + return impl_->error; +} + +std::string Database::escape(const std::string &value) const { + std::lock_guard lock(impl_->mutex); + if (impl_->connection == nullptr && !const_cast(this)->connect()) return value; + if (impl_->connection == nullptr) return value; + std::string escaped; + escaped.resize(value.size() * 2 + 1); + const unsigned long size = mysql_real_escape_string(impl_->connection, escaped.data(), + value.data(), + static_cast(value.size())); + escaped.resize(size); + return escaped; +} + +bool Database::execute(const std::string &sql) { + std::lock_guard lock(impl_->mutex); + if (impl_->connection == nullptr && !const_cast(this)->connect()) return false; + if (mysql_query(impl_->connection, sql.c_str()) != 0) { + impl_->error = mysql_error(impl_->connection); + return false; + } + return true; +} + +std::vector> Database::query(const std::string &sql) { + std::vector> rows; + std::lock_guard lock(impl_->mutex); + if (impl_->connection == nullptr && !connect()) return rows; + if (mysql_query(impl_->connection, sql.c_str()) != 0) { + impl_->error = mysql_error(impl_->connection); + return rows; + } + MYSQL_RES *result = mysql_store_result(impl_->connection); + if (result == nullptr) return rows; + const int count = mysql_num_fields(result); + MYSQL_FIELD *fields = mysql_fetch_fields(result); + MYSQL_ROW row = nullptr; + while ((row = mysql_fetch_row(result)) != nullptr) { + unsigned long *lengths = mysql_fetch_lengths(result); + std::map item; + for (int i = 0; i < count; ++i) item[fields[i].name] = row[i] == nullptr ? "" : std::string(row[i], lengths[i]); + rows.push_back(std::move(item)); + } + mysql_free_result(result); + return rows; +} + +long long Database::last_insert_id() const { + std::lock_guard lock(impl_->mutex); + return impl_->connection == nullptr ? 0 : static_cast(mysql_insert_id(impl_->connection)); +} +ProjectCatalog::ProjectCatalog(const PathConfig &paths) : paths_(paths) { reload(); } + +Json ProjectCatalog::parse_json_file(const std::string &path) const { + const std::string text = read_text_file(path); + if (text.empty()) return Json::object(); + try { + return Json::parse(text); + } catch (...) { + return Json::object(); + } +} + +std::vector ProjectCatalog::read_script_list(const std::string &root, const std::vector &items) const { + std::vector result; + for (const auto &item : items) { + const std::string content = read_text_file(join_path(root, item)); + if (!content.empty()) result.push_back(content); + } + return result; +} + +std::vector> ProjectCatalog::read_named_scripts(const std::string &root, const std::vector &items) const { + std::vector> result; + for (const auto &item : items) { + const std::string path = join_path(root, item); + const std::string content = read_text_file(path); + if (!content.empty()) result.emplace_back(path, content); + } + return result; +} + +const ProjectCatalog::VersionData *ProjectCatalog::find_version(const std::string &requested) const { + if (!requested.empty()) { + const auto found = versions_.find(requested); + if (found != versions_.end()) return &found->second; + } + const auto latest = versions_.find(newest_version_); + return latest == versions_.end() ? nullptr : &latest->second; +} + +void ProjectCatalog::load_custom_client_scripts(VersionData &data) { + const fs::path root(paths_.client_user_script_root); + if (!fs::exists(root)) return; + for (const auto &entry : fs::directory_iterator(root)) { + if (!entry.is_directory()) continue; + const fs::path config = entry.path() / "config"; + if (!fs::exists(config)) continue; + const auto scripts = split_csv_config(config.string()); + data.custom_client_scripts[entry.path().filename().string()] = read_script_list(entry.path().string(), scripts); + } +} + +void ProjectCatalog::load_client_versions() { + const fs::path root(paths_.script_root); + if (!fs::exists(root)) return; + for (const auto &entry : fs::directory_iterator(root)) { + if (!entry.is_directory()) continue; + const fs::path config_file = entry.path() / "FileConfig.json"; + if (!fs::exists(config_file)) continue; + VersionData data; + const std::string dir_name = entry.path().filename().string(); + data.name = dir_name; + if (dir_name.rfind("Script", 0) == 0) { + data.name = dir_name.substr(6); + if (!data.name.empty() && (data.name.front() == '_' || data.name.front() == '-')) data.name.erase(data.name.begin()); + } + if (data.name.empty()) data.name = dir_name; + const Json root_json = parse_json_file(config_file.string()); + if (root_json.contains("BaseScript") && root_json["BaseScript"].is_array()) { + std::vector names; + for (const auto &item : root_json["BaseScript"].as_array()) names.push_back(item.to_string_or("")); + data.base_scripts = read_script_list(entry.path().string(), names); + } + if (root_json.contains("CSBaseScript") && root_json["CSBaseScript"].is_array()) { + std::vector names; + for (const auto &item : root_json["CSBaseScript"].as_array()) names.push_back(item.to_string_or("")); + data.cs_base_scripts = read_script_list(entry.path().string(), names); + } + if (root_json.contains("ProjectScript") && root_json["ProjectScript"].is_object()) { + for (const auto &project_entry : root_json["ProjectScript"].as_object()) { + ProjectDefinition project; + project.name = project_entry.first; + const Json &project_json = project_entry.second; + if (project_json.contains("Script") && project_json["Script"].is_array()) { + for (const auto &item : project_json["Script"].as_array()) project.script_paths.push_back(item.to_string_or("")); + } + if (project_json.contains("Info") && project_json["Info"].is_object()) + project.info = project_json["Info"].as_object(); + else if (project_json.contains("info") && project_json["info"].is_object()) + project.info = project_json["info"].as_object(); + project.price = project_json["price"].to_int_or(0); + project.is_private = project_json["Private"].to_bool_or(false); + project.img_path = project_json["imgPath"].to_string_or(""); + data.projects[project.name] = project; + data.project_scripts[project.name] = read_script_list(entry.path().string(), project.script_paths); + } + } + const std::string manifest_path = join_path(join_path(paths_.file_root, "DP-S_Script"), "Script" + data.name + "/_DPS_/FileConfig.json"); + data.dps_manifest = parse_json_file(manifest_path); + load_custom_client_scripts(data); + const std::string version_name = data.name; + versions_[data.name] = std::move(data); + newest_version_ = std::max(newest_version_, version_name); + } +} + +void ProjectCatalog::reload() { + versions_.clear(); + newest_version_.clear(); + load_client_versions(); +} + +std::optional ProjectCatalog::get_project(const std::string &name, const std::string &version) const { + const VersionData *data = find_version(version); + if (data == nullptr) return std::nullopt; + const auto it = data->projects.find(name); + return it == data->projects.end() ? std::optional{} : std::optional{it->second}; +} + +Json ProjectCatalog::public_project_info() const { + Json result = Json::object(); + const VersionData *data = find_version(""); + if (data == nullptr) return result; + for (const auto &entry : data->projects) { + if (entry.second.is_private) continue; + Json info = map_to_json_object(entry.second.info); + info["ProjectPrice"] = entry.second.price; + result[entry.first] = info; + } + return result; +} + +Json ProjectCatalog::all_project_info() const { + Json result = Json::object(); + const VersionData *data = find_version(""); + if (data == nullptr) return result; + for (const auto &entry : data->projects) { + Json info = map_to_json_object(entry.second.info); + info["ProjectPrice"] = entry.second.price; + result[entry.first] = info; + } + return result; +} + +int ProjectCatalog::project_price(const std::string &name) const { + const auto project = get_project(name); + return project.has_value() ? project->price : 0; +} + +std::string ProjectCatalog::project_img_path(const std::string &name) const { + const auto project = get_project(name); + return project.has_value() ? project->img_path : ""; +} + +std::vector ProjectCatalog::base_script_contents(const std::string &version, bool cs_mode) const { + const VersionData *data = find_version(version); + if (data == nullptr) return {}; + return cs_mode && !data->cs_base_scripts.empty() ? data->cs_base_scripts : data->base_scripts; +} + +std::vector ProjectCatalog::project_script_contents(const std::string &project_name, const std::string &version) const { + const VersionData *data = find_version(version); + if (data == nullptr) return {}; + const auto it = data->project_scripts.find(project_name); + return it == data->project_scripts.end() ? std::vector{} : it->second; +} + +std::vector ProjectCatalog::custom_client_scripts(const std::string &ip, const std::string &version) const { + const VersionData *data = find_version(version); + if (data == nullptr) return {}; + const auto it = data->custom_client_scripts.find(ip); + return it == data->custom_client_scripts.end() ? std::vector{} : it->second; +} + +std::vector> ProjectCatalog::dps_service_scripts(const std::string &ip) const { + std::vector> result; + const std::string base_root = join_path(paths_.file_root, "dpsScript"); + std::string base_name = "base"; + const std::vector special_ips = {"112.12.194.152", "202.189.14.166", "45.125.44.2", "110.42.110.159", "202.189.14.111"}; + if (std::find(special_ips.begin(), special_ips.end(), ip) != special_ips.end()) base_name = "base3"; + const fs::path base_config = fs::path(base_root) / base_name / "config"; + if (fs::exists(base_config)) { + const auto names = split_csv_config(base_config.string()); + auto items = read_named_scripts((fs::path(base_root) / base_name).string(), names); + result.insert(result.end(), items.begin(), items.end()); + } + const fs::path common = fs::path(base_root) / u8"user" / u8"通用" / "main.nut"; + if (fs::exists(common)) result.emplace_back(common.string(), read_text_file(common.string())); + const fs::path custom_root = fs::path(base_root) / "user" / ip; + const fs::path custom_config = custom_root / "config"; + if (fs::exists(custom_config)) { + const auto names = split_csv_config(custom_config.string()); + auto items = read_named_scripts(custom_root.string(), names); + result.insert(result.end(), items.begin(), items.end()); + } + return result; +} + +std::vector> ProjectCatalog::dps_service_scripts_new(const std::string &ip, const std::string &version) const { + std::vector> result; + const VersionData *data = find_version(version); + if (data == nullptr) return result; + const fs::path custom_root = fs::path(paths_.file_root) / "dpsScript" / "user" / ip; + const fs::path custom_config = custom_root / "config"; + if (fs::exists(custom_config)) { + const auto names = split_csv_config(custom_config.string()); + auto items = read_named_scripts(custom_root.string(), names); + result.insert(result.end(), items.begin(), items.end()); + } + return result; +} + +std::string ProjectCatalog::newest_version() const { return newest_version_; } + +long long ProjectCatalog::base_update_time(const std::string &version) const { + const VersionData *data = find_version(version); + if (data == nullptr) return 0; + const fs::path root = fs::path(paths_.file_root) / "DP-S_Script" / ("Script" + data->name) / "_DPS_"; + return latest_mtime_ms(root); +} + +Application::Application(AppConfig config) + : config_(std::move(config)), crypto_(config_.jwt.secret), database_(config_.database), catalog_(config_.paths) { + if (!database_.connect()) { + throw std::runtime_error("failed to connect database: " + database_.last_error()); + } + register_routes(); +} + +Response Application::json_response(int status, const Json &body) const { + Response response; + response.status = status; + response.body = body.dump(); + return response; +} + +Response Application::common_success(const Json &data, const std::string &message, int status) const { + Json result = Json::object(); + result["code"] = 200; + result["message"] = message; + result["data"] = data; + return json_response(status, result); +} + +Response Application::common_failed(int code, const std::string &message, int http_status) const { + Json result = Json::object(); + result["code"] = code; + result["message"] = message; + result["data"] = Json(); + return json_response(http_status, result); +} + +Response Application::file_response(const fs::path &path, const std::string &download_name) const { + if (!fs::exists(path) || !fs::is_regular_file(path)) return common_failed(404, "file not found", 404); + Response response; + response.status = 200; + response.content_type = guess_content_type(path_to_utf8_string(path)); + response.binary_body = read_binary_file(path); + const std::string name = download_name.empty() ? path_to_utf8_string(path.filename()) : download_name; + response.headers["Content-Disposition"] = "attachment; filename=\"" + name + "\""; + return response; +} +std::string Application::request_ip(const Request &request) const { + if (!config_.server.trust_proxy) return request.remote_addr; + const std::string forwarded = request.header(config_.server.proxy_header); + if (!forwarded.empty()) { + const std::size_t comma = forwarded.find(','); + const std::string ip = trim(forwarded.substr(0, comma)); + if (!ip.empty()) return ip; + } + return request.remote_addr; +} + +std::vector Application::random_key() const { + std::vector key(20, 1); + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dist(1, 9); + for (int &item : key) item = dist(gen); + return key; +} + +std::string Application::format_date_now() const { return format_now(); } +std::string Application::add_days(const std::string &iso_date, int days) const { return add_days_impl(iso_date, days); } + +std::string Application::make_random_name() const { + static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dist(0, static_cast(sizeof(chars) - 2)); + std::string output; + for (int i = 0; i < 8; ++i) output.push_back(chars[dist(gen)]); + return output; +} + +Json Application::parse_body_or_empty(const Request &request) const { + if (request.body.empty()) return Json::object(); + try { + return Json::parse(request.body); + } catch (...) { + Json result = Json::object(); + for (const auto &entry : parse_query_string(request.body)) result[entry.first] = entry.second; + return result; + } +} + +std::optional Application::current_user(const Request &request) const { + if (request.current_user.has_value() && !request.current_user->empty()) return request.current_user; + return std::nullopt; +} +std::optional Application::find_user_by_username(const std::string &username) { + const std::string sql = + "SELECT id, username, password, gold, level, qq, name, img, client_key, server_key FROM sys_user WHERE username='" + + database_.escape(username) + "' LIMIT 1"; + const auto rows = database_.query(sql); + if (rows.empty()) return std::nullopt; + const Row &row = rows.front(); + User user; + user.id = row.count("id") ? std::stoi(row.at("id")) : 0; + user.username = row.at("username"); + user.password = row.at("password"); + user.gold = row.count("gold") ? std::stoi(row.at("gold")) : 0; + user.level = row.count("level") ? std::stoi(row.at("level")) : 1; + user.qq = row.count("qq") ? row.at("qq") : ""; + user.name = row.count("name") ? row.at("name") : ""; + user.img = row.count("img") ? row.at("img") : ""; + user.client_key = row.count("client_key") ? row.at("client_key") : ""; + user.server_key = row.count("server_key") ? row.at("server_key") : ""; + return user; +} + +bool Application::username_exists(const std::string &username) { return find_user_by_username(username).has_value(); } + +bool Application::create_user(const std::string &username, const std::string &password, const std::string &qq) { + const std::string encoded_password = crypto_.hash_password(password); + if (encoded_password.empty()) return false; + const std::string sql = + "INSERT INTO sys_user (username, password, create_time, update_time, gold, qq, name) VALUES ('" + + database_.escape(username) + "','" + database_.escape(encoded_password) + + "', NOW(), NOW(), 0, '" + database_.escape(qq) + "', '" + database_.escape(make_random_name()) + "')"; + return database_.execute(sql); +} + +bool Application::update_password(const std::string &username, const std::string &qq, const std::string &password) { + auto user = find_user_by_username(username); + if (!user.has_value() || user->qq != qq) return false; + const std::string encoded_password = crypto_.hash_password(password); + if (encoded_password.empty()) return false; + return database_.execute("UPDATE sys_user SET password='" + database_.escape(encoded_password) + + "', update_time=NOW() WHERE username='" + database_.escape(username) + "'"); +} + +bool Application::update_user_row(const User &user) { + const std::string sql = + "UPDATE sys_user SET gold=" + std::to_string(user.gold) + ", name='" + database_.escape(user.name) + + "', img='" + database_.escape(user.img) + "', client_key='" + database_.escape(user.client_key) + + "', server_key='" + database_.escape(user.server_key) + "', update_time=NOW() WHERE id=" + std::to_string(user.id); + return database_.execute(sql); +} + +std::vector Application::servers_for_user(const std::string &username) { + std::vector result; + const auto rows = database_.query("SELECT user_name, server_ip, account, password, port FROM user_and_server WHERE user_name='" + database_.escape(username) + "'"); + for (const auto &row : rows) result.push_back(UserAndServer{row.at("user_name"), row.at("server_ip"), row.at("account"), row.at("password"), row.at("port")}); + return result; +} + +std::optional Application::find_server(const std::string &username, const std::string &ip) { + const auto rows = database_.query("SELECT user_name, server_ip, account, password, port FROM user_and_server WHERE user_name='" + database_.escape(username) + "' AND server_ip='" + database_.escape(ip) + "' LIMIT 1"); + if (rows.empty()) return std::nullopt; + const Row &row = rows.front(); + return UserAndServer{row.at("user_name"), row.at("server_ip"), row.at("account"), row.at("password"), row.at("port")}; +} + +bool Application::upsert_server(const UserAndServer &server) { + if (find_server(server.user_name, server.server_ip).has_value()) { + return database_.execute("UPDATE user_and_server SET account='" + database_.escape(server.account) + + "', password='" + database_.escape(server.password) + "', port='" + database_.escape(server.port) + + "' WHERE user_name='" + database_.escape(server.user_name) + "' AND server_ip='" + database_.escape(server.server_ip) + "'"); + } + return database_.execute("INSERT INTO user_and_server (user_name, server_ip, account, password, port) VALUES ('" + + database_.escape(server.user_name) + "','" + database_.escape(server.server_ip) + "','" + + database_.escape(server.account) + "','" + database_.escape(server.password) + "','" + database_.escape(server.port) + "')"); +} + +bool Application::delete_server(const std::string &username, const std::string &ip) { + return database_.execute("DELETE FROM user_and_server WHERE user_name='" + database_.escape(username) + "' AND server_ip='" + database_.escape(ip) + "'"); +} + +std::vector Application::projects_for_ip(const std::string &ip, bool require_open) { + std::vector result; + std::string sql = + "SELECT server_ip, project_name, user_name, open, DATE_FORMAT(expiration_date, '%Y-%m-%d %H:%i:%s') AS expiration_date FROM user_server_project WHERE server_ip='" + + database_.escape(ip) + "' AND expiration_date >= NOW()"; + if (require_open) sql += " AND open=1"; + const auto rows = database_.query(sql); + for (const auto &row : rows) result.push_back(UserServerProject{row.at("server_ip"), row.at("project_name"), row.at("user_name"), row.at("expiration_date"), row.count("open") ? std::stoi(row.at("open")) : 1}); + return result; +} + +std::vector Application::projects_for_user(const std::string &username) { + std::vector result; + const auto rows = database_.query( + "SELECT server_ip, project_name, user_name, open, DATE_FORMAT(expiration_date, '%Y-%m-%d %H:%i:%s') AS expiration_date FROM user_server_project WHERE user_name='" + + database_.escape(username) + "' AND expiration_date >= NOW()"); + for (const auto &row : rows) result.push_back(UserServerProject{row.at("server_ip"), row.at("project_name"), row.at("user_name"), row.at("expiration_date"), row.count("open") ? std::stoi(row.at("open")) : 1}); + return result; +} + +std::optional Application::find_project_binding(const std::string &ip, const std::string &project_name) { + const auto rows = database_.query( + "SELECT server_ip, project_name, user_name, open, DATE_FORMAT(expiration_date, '%Y-%m-%d %H:%i:%s') AS expiration_date FROM user_server_project WHERE server_ip='" + + database_.escape(ip) + "' AND project_name='" + database_.escape(project_name) + "' LIMIT 1"); + if (rows.empty()) return std::nullopt; + const Row &row = rows.front(); + return UserServerProject{row.at("server_ip"), row.at("project_name"), row.at("user_name"), row.at("expiration_date"), row.count("open") ? std::stoi(row.at("open")) : 1}; +} + +bool Application::add_or_extend_project(const std::string &ip, const std::string &project_name, int days, const std::string &username) { + const auto existing = find_project_binding(ip, project_name); + if (!existing.has_value()) { + return database_.execute("INSERT INTO user_server_project (server_ip, project_name, expiration_date, user_name, open) VALUES ('" + database_.escape(ip) + + "','" + database_.escape(project_name) + "', DATE_ADD(NOW(), INTERVAL " + std::to_string(days) + " DAY), '" + + database_.escape(username) + "', 1)"); + } + const std::string next = add_days(existing->expiration_date, days); + return database_.execute("UPDATE user_server_project SET expiration_date='" + database_.escape(next) + "', user_name='" + database_.escape(username) + + "' WHERE server_ip='" + database_.escape(ip) + "' AND project_name='" + database_.escape(project_name) + "'"); +} + +bool Application::update_project_open(const std::string &ip, const std::string &project_name, int open_value) { + return database_.execute("UPDATE user_server_project SET open=" + std::to_string(open_value) + " WHERE server_ip='" + database_.escape(ip) + + "' AND project_name='" + database_.escape(project_name) + "'"); +} + +bool Application::insert_log(const std::string &username, int type, const std::string &content) { + return database_.execute("INSERT INTO `log` (`name`, `log`, `type`, `time`) VALUES ('" + database_.escape(username) + "','" + + database_.escape(content) + "'," + std::to_string(type) + ", NOW())"); +} + +void Application::register_routes() { + using Member = Response (Application::*)(const Request &); + auto add = [this](const std::string &method, const std::string &path, bool auth, Member member) { + router_.add(method, path, auth, [this, auth, member](const Request &request) { + Request copy = request; + if (auth) { + const std::string header = copy.header("authorization"); + if (header.rfind("Bearer ", 0) != 0) return common_failed(401, "暂未登录或 token 已经过期", 401); + const auto user = crypto_.validate_jwt(header.substr(7)); + if (!user.has_value() || user->empty()) return common_failed(401, "暂未登录或 token 已经过期", 401); + copy.current_user = user.value(); + } + return (this->*member)(copy); + }); + }; + + add("POST", "/login", false, &Application::handle_login); + add("POST", "/register", false, &Application::handle_register); + add("POST", "/changePassword", false, &Application::handle_change_password); + add("POST", "/rindro/getInfo", true, &Application::handle_get_info); + add("POST", "/rindro/addServer", true, &Application::handle_add_server); + add("POST", "/rindro/deleteServer", true, &Application::handle_delete_server); + add("POST", "/rindro/getServerList", true, &Application::handle_get_server_list); + add("POST", "/rindro/getServer", true, &Application::handle_get_server); + add("POST", "/rindro/getProjectInfoList", true, &Application::handle_get_project_info_list); + add("POST", "/rindro/getUserProList", true, &Application::handle_get_user_project_list); + add("POST", "/rindro/getServerProList", true, &Application::handle_get_server_project_list); + add("POST", "/rindro/buy", true, &Application::handle_buy); + add("GET", "/rindro/addgold", false, &Application::handle_add_gold); + add("POST", "/rindro/transfer", true, &Application::handle_transfer); + add("POST", "/rindro/setKey", true, &Application::handle_set_key); + add("POST", "/rindro/setproopen", true, &Application::handle_set_project_open); + add("POST", "/rindro/download", false, &Application::handle_rindro_download); + add("GET", "/rindro/download/sh", false, &Application::handle_get_version); + add("GET", "/rindro/download/c", false, &Application::handle_get_version); + add("GET", "/rindro/download/d", false, &Application::handle_get_version); + add("GET", "/rindro/download/djson", false, &Application::handle_get_version); + add("GET", "/rindro/download/dsh", false, &Application::handle_get_version); + add("GET", "/rindro/download/ddsh", false, &Application::handle_get_version); + add("GET", "/rindro/download/jdk", false, &Application::handle_get_version); + add("GET", "/rindro/download/jar", false, &Application::handle_get_version); + add("POST", "/rindro/download/all", false, &Application::handle_get_version); + add("GET", "/rindro/download/gr", false, &Application::handle_get_version); + add("GET", "/rindro/getversion", false, &Application::handle_get_version); + add("GET", "/rindro/getimg/{project}/{img}", false, &Application::handle_get_version); + add("GET", "/rindro/getthumb/{project}/{img}", false, &Application::handle_get_version); + add("GET", "/rindro/getmp42/{project}/{img}", false, &Application::handle_get_version); + add("GET", "/rindro/getmp4/{project}/{img}", false, &Application::handle_get_version); + add("GET", "/dps/getversion", false, &Application::handle_get_version); + add("GET", "/dps/getclientversion", false, &Application::handle_get_version); + add("GET", "/dps/getlist", false, &Application::handle_get_version); + add("GET", "/dps/getadvertisement", false, &Application::handle_get_version); + add("POST", "/dps/download/{colonPath}", false, &Application::handle_get_version); + add("POST", "/dps/download2/{colonPath}", false, &Application::handle_get_version); + add("GET", "/dps/download3/{colonPath}", false, &Application::handle_get_version); + add("GET", "/script/client", false, &Application::handle_script_client); + add("GET", "/script/client2", false, &Application::handle_script_client2); + add("GET", "/script/getzdy", false, &Application::handle_script_getzdy); + add("POST", "/script/getpro", false, &Application::handle_script_getpro); + add("GET", "/script/getservice", false, &Application::handle_script_getservice); + add("GET", "/script/getBaseUpdateTime", false, &Application::handle_script_base_update_time); + add("GET", "/script/getBaseSutZip", false, &Application::handle_script_base_sut); + add("GET", "/script/getservicenew", false, &Application::handle_script_getservice_new); + add("GET", "/api/videos/{project}/{videoName}", false, &Application::handle_video_placeholder); + add("GET", "/zf/callback", false, &Application::handle_payment_placeholder); + add("POST", "/zf/create", true, &Application::handle_payment_placeholder); +} +Response Application::handle_login(const Request &request) { + const Json body = parse_body_or_empty(request); + const std::string username = body["username"].to_string_or(""); + const std::string password = body["password"].to_string_or(""); + const auto user = find_user_by_username(username); + if (!user.has_value() || !crypto_.verify_password(password, user->password)) return common_failed(401, "用户名或密码错误", 401); + Json data = Json::object(); + data["token"] = "Bearer " + crypto_.issue_jwt(user->username, config_.jwt.expiration_seconds); + data["username"] = user->username; + Json roles = Json::array(); + roles.push_back("ROLE_ALL"); + data["roles"] = roles; + return json_response(200, data); +} + +Response Application::handle_register(const Request &request) { + const Json body = parse_body_or_empty(request); + const std::string username = body["username"].to_string_or(""); + const std::string password = body["password"].to_string_or(""); + const std::string qq = body["qq"].to_string_or(""); + if (username.empty()) return common_failed(201, "用户名为空", 201); + if (password.empty()) return common_failed(201, "密码为空", 201); + if (qq.empty()) return common_failed(201, "安全口令为空", 201); + if (username_exists(username)) return common_failed(201, "用户名已存在", 201); + return create_user(username, password, qq) ? common_success("注册成功") : common_failed(500, "注册失败"); +} + +Response Application::handle_change_password(const Request &request) { + const Json body = parse_body_or_empty(request); + const std::string username = body["username"].to_string_or(""); + const std::string password = body["password"].to_string_or(""); + const std::string qq = body["qq"].to_string_or(""); + if (username.empty()) return common_failed(201, "用户名为空", 201); + if (password.empty()) return common_failed(201, "密码为空", 201); + if (qq.empty()) return common_failed(201, "安全口令为空", 201); + return update_password(username, qq, password) ? common_success("修改成功") : common_failed(500, "验证信息错误"); +} + +Response Application::handle_get_info(const Request &request) { + const auto user = find_user_by_username(current_user(request).value_or("")); + if (!user.has_value()) return common_failed(404, "用户不存在", 404); + Json body = Json::object(); + body["userpoints"] = user->gold; + body["username"] = user->username; + body["displayName"] = user->name; + body["useravatar"] = user->img; + body["level"] = user->level; + body["uservip"] = membership_label(user->level); + body["usercash"] = Json(); + return common_success(body); +} + +Response Application::handle_add_server(const Request &request) { + const Json body = parse_body_or_empty(request); + UserAndServer row{current_user(request).value_or(""), body["ip"].to_string_or(""), body["username"].to_string_or(""), body["password"].to_string_or(""), body["port"].to_string_or("")}; + return upsert_server(row) ? common_success("添加成功") : common_failed(500, "添加失败"); +} + +Response Application::handle_delete_server(const Request &request) { + const Json body = parse_body_or_empty(request); + return delete_server(current_user(request).value_or(""), body["ip"].to_string_or("")) ? common_success("删除成功") : common_failed(500, "删除失败"); +} + +Response Application::handle_get_server_list(const Request &request) { + Json result = Json::array(); + for (const auto &server : servers_for_user(current_user(request).value_or(""))) { + Json item = Json::object(); + item["serverIp"] = server.server_ip; + item["userName"] = server.account; + item["password"] = server.password; + item["port"] = server.port; + result.push_back(item); + } + return common_success(result); +} + +Response Application::handle_get_server(const Request &request) { + const Json body = parse_body_or_empty(request); + const auto server = find_server(current_user(request).value_or(""), body["ip"].to_string_or("")); + if (!server.has_value()) return common_success(Json()); + Json item = Json::object(); + item["userName"] = server->user_name; + item["serverIp"] = server->server_ip; + item["account"] = server->account; + item["password"] = server->password; + item["port"] = server->port; + return common_success(item); +} + +Response Application::handle_get_project_info_list(const Request &) { + const Json data = catalog_.public_project_info(); + return common_success(data); +} + +Response Application::handle_get_user_project_list(const Request &request) { + const Json body = parse_body_or_empty(request); + const std::string ip = body["ip"].to_string_or(""); + const Json all = catalog_.all_project_info(); + Json result = Json::object(); + for (const auto &binding : projects_for_ip(ip)) { + Json info = all[binding.project_name]; + if (info.is_null()) continue; + info["endTime"] = binding.expiration_date; + info["open"] = binding.open; + result[binding.project_name] = info; + } + return common_success(result); +} + +Response Application::handle_get_server_project_list(const Request &request) { + const Json body = parse_body_or_empty(request); + const std::string ip = body["ip"].to_string_or(""); + if (!find_server(current_user(request).value_or(""), ip).has_value()) return common_success(Json()); + Json items = Json::array(); + for (const auto &binding : projects_for_ip(ip)) { + Json item = Json::object(); + item["serverIp"] = binding.server_ip; + item["projectName"] = binding.project_name; + item["expirationDate"] = binding.expiration_date; + item["userName"] = binding.user_name; + item["open"] = binding.open; + items.push_back(item); + } + return common_success(items); +} + +Response Application::handle_buy(const Request &request) { + const Json body = parse_body_or_empty(request); + const std::string username = current_user(request).value_or(""); + const std::string ip = body["ip"].to_string_or(""); + const std::string project_name = body["projectName"].to_string_or(""); + if (!find_server(username, ip).has_value()) return common_failed(500, "账号下不存在此服务器"); + auto user = find_user_by_username(username); + if (!user.has_value()) return common_failed(500, "用户不存在"); + const int price = catalog_.project_price(project_name); + if (price > 0 && user->gold < price) return common_failed(500, "积分不足"); + if (price > 0) { + user->gold -= price; + update_user_row(*user); + } + if (!add_or_extend_project(ip, project_name, 31, username)) return common_failed(500, "购买失败"); + insert_log(username, price > 0 ? 3 : 2, "购买项目 " + project_name + " IP " + ip); + Json result = Json::object(); + result["info"] = "购买成功"; + const auto binding = find_project_binding(ip, project_name); + result["endTime"] = binding.has_value() ? binding->expiration_date : add_days(format_date_now(), 31); + return common_success(result); +} + +Response Application::handle_add_gold(const Request &request) { + const std::string username = request.query_value("userName"); + const int requested = request.query_value("gold").empty() ? 0 : std::stoi(request.query_value("gold")); + auto user = find_user_by_username(username); + if (!user.has_value()) return common_failed(500, "充值失败"); + int gold = requested; + if (gold == 200) gold = 220; + if (gold == 500) gold = 570; + user->gold += gold; + update_user_row(*user); + insert_log(username, 1, "充值 " + std::to_string(gold) + " 成功"); + return common_success("充值成功"); +} + +Response Application::handle_transfer(const Request &request) { + const Json body = parse_body_or_empty(request); + const std::string from = current_user(request).value_or(""); + const std::string to = body["username"].to_string_or(""); + const int gold = body["gold"].to_int_or(0); + std::lock_guard lock(transfer_mutex_); + auto from_user = find_user_by_username(from); + auto to_user = find_user_by_username(to); + if (!from_user.has_value() || !to_user.has_value()) return common_success("被转让用户不存在,请检查用户名"); + if (from_user->gold < gold) return common_success("余额不足"); + from_user->gold -= gold; + to_user->gold += gold; + update_user_row(*from_user); + update_user_row(*to_user); + insert_log(from, 4, from + " 向 " + to + " 转让 " + std::to_string(gold) + " 积分"); + return common_success("转让完毕"); +} + +Response Application::handle_set_key(const Request &request) { + auto user = find_user_by_username(current_user(request).value_or("")); + if (!user.has_value()) return common_success("请重新登录后再试"); + const Json body = parse_body_or_empty(request); + if (body.contains("clientkey")) user->client_key = body["clientkey"].to_string_or(user->client_key); + if (body.contains("serverkey")) user->server_key = body["serverkey"].to_string_or(user->server_key); + return update_user_row(*user) ? common_success("设置成功") : common_success("设置失败"); +} + +Response Application::handle_set_project_open(const Request &request) { + const Json body = parse_body_or_empty(request); + const std::string ip = body["ip"].to_string_or(""); + const std::string project = body["projectName"].to_string_or(""); + const int open = body["open"].to_int_or(0); + if (!find_server(current_user(request).value_or(""), ip).has_value()) return common_success("账号下不存在此项目,请检查项目所有权是否为该账号"); + return update_project_open(ip, project, open) ? common_success("操作成功") : common_success("账号下不存在此项目,请检查项目所有权是否为该账号"); +} + +Response Application::handle_rindro_download(const Request &request) { + const Json body = parse_body_or_empty(request); + const std::string project_name = trim(body["projectName"].to_string_or("")); + if (project_name.empty()) return common_failed(404, "file not found", 404); + + const fs::path target = path_from_utf8(config_.paths.file_root) / "download" / "profile" / path_from_utf8(project_name + ".zip"); + if (!fs::exists(target) || !fs::is_regular_file(target)) { + std::clog << "[download] profile package missing project=" << project_name + << " path=" << path_to_utf8_string(target) << '\n'; + return common_failed(404, "file not found", 404); + } + return file_response(target, project_name + ".zip"); +} + +Response Application::handle_static_download(const std::string &disk_path, const std::string &download_name) const { return file_response(disk_path, download_name); } + +Response Application::handle_get_image(const Request &request, bool) { + const std::string project_name = request.path_param("project"); + const std::string img = request.path_param("img"); + const std::string img_path = catalog_.project_img_path(project_name); + if (img_path.empty()) return common_failed(404, "file not found", 404); + return file_response(join_path(join_path(config_.paths.file_root, "Script/Project/" + img_path + "/img"), img + ".png")); +} + +Response Application::handle_get_mp42(const Request &request) { + const std::string project_name = request.path_param("project"); + const std::string img = request.path_param("img"); + const std::string img_path = catalog_.project_img_path(project_name); + if (img_path.empty()) return common_failed(404, "file not found", 404); + return file_response(join_path(join_path(config_.paths.file_root, "Script/Project/" + img_path + "/vid"), img + ".mp4")); +} +Response Application::handle_get_version(const Request &request) { + const std::string path = request.path; + if (path == "/rindro/download/sh") return file_response(join_path(config_.paths.file_root, "re.sh"), "re.sh"); + if (path == "/rindro/download/c") return file_response(join_path(config_.paths.file_root, "download/ndpsm_svr"), "ndpsm_svr"); + if (path == "/rindro/download/d") return file_response(join_path(config_.paths.file_root, "download/DPS_Login_Gateway"), "DPS_Login_Gateway"); + if (path == "/rindro/download/djson") return file_response(join_path(config_.paths.file_root, "download/config.json"), "config.json"); + if (path == "/rindro/download/dsh") return file_response(join_path(config_.paths.file_root, "download/DpsLoginRestartSh"), "DpsLoginRestartSh"); + if (path == "/rindro/download/ddsh") return file_response(join_path(config_.paths.file_root, "download/DpsLoginDownloadSh"), "DpsLoginDownloadSh"); + if (path == "/rindro/download/jdk") return file_response(join_path(config_.paths.file_root, "jdk"), "jdk"); + if (path == "/rindro/download/jar") return file_response(join_path(config_.paths.file_root, "download/RT.tar.gz"), "RT.tar.gz"); + if (path == "/rindro/download/all") { + try { + const Json body = parse_body_or_empty(request); + const Json map = Json::parse(read_text_file(config_.paths.file_map)); + const std::string target = map[body["key"].to_string_or("")].to_string_or(""); + if (target.empty() || !fs::exists(target) || !fs::is_regular_file(target)) return common_failed(404, "无下载文件", 404); + return file_response(target); + } catch (...) { + return common_failed(404, "无下载文件", 404); + } + } + if (path == "/rindro/download/gr") { + try { + const Json map = Json::parse(read_text_file(config_.paths.file_map)); + const std::string target = map[request.query_value("body")].to_string_or(""); + if (target.empty() || !fs::exists(target) || !fs::is_regular_file(target)) return common_failed(404, "无下载文件", 404); + return file_response(target); + } catch (...) { + return common_failed(404, "无下载文件", 404); + } + } + if (path == "/rindro/getversion") { + Response response; + response.content_type = "text/plain; charset=utf-8"; + response.body = read_text_file(join_path(config_.paths.file_root, "download/version")); + return response; + } + if (path.rfind("/rindro/getimg/", 0) == 0) return handle_get_image(request, false); + if (path.rfind("/rindro/getthumb/", 0) == 0) return handle_get_image(request, true); + if (path.rfind("/rindro/getmp42/", 0) == 0 || path.rfind("/rindro/getmp4/", 0) == 0) return handle_get_mp42(request); + if (path == "/dps/getversion") return handle_dps_version("Application/Proj.ifo"); + if (path == "/dps/getclientversion") return handle_dps_version("Marketplace/Proj.ifo"); + if (path == "/dps/getlist") return handle_dps_list("Script", "Proj.ifo"); + if (path == "/dps/getadvertisement") return handle_dps_list("Advertisement", "info.ifo"); + if (path.rfind("/dps/download/", 0) == 0) return handle_dps_download(request, join_path(config_.paths.dps_root, "Script")); + if (path.rfind("/dps/download2/", 0) == 0) return handle_dps_download(request, join_path(config_.paths.dps_root, "Application")); + if (path.rfind("/dps/download3/", 0) == 0) return handle_dps_download(request, join_path(config_.paths.dps_root, "Advertisement")); + return common_failed(404, "not found", 404); +} + +Response Application::handle_dps_version(const std::string &relative_file) const { + Response response; + response.content_type = "text/plain; charset=utf-8"; + response.body = read_text_file(join_path(config_.paths.dps_root, relative_file)); + return response; +} + +Response Application::handle_dps_list(const std::string &relative_dir, const std::string &metadata_file) const { + Json list = Json::array(); + const fs::path root = fs::path(config_.paths.dps_root) / relative_dir; + if (fs::exists(root)) { + for (const auto &entry : fs::directory_iterator(root)) { + if (!entry.is_directory()) continue; + const std::string text = read_text_file((entry.path() / metadata_file).string()); + if (text.empty()) continue; + try { list.push_back(Json::parse(text)); } catch (...) { list.push_back(text); } + } + } + Json result = Json::object(); + result["list"] = list; + Response response; + response.content_type = "application/json; charset=utf-8"; + response.body = result.dump(); + return response; +} + +Response Application::handle_dps_download(const Request &request, const std::string &base_dir) const { + std::string relative = request.path_param("colonPath"); + relative = url_decode(relative); + std::replace(relative.begin(), relative.end(), ':', '/'); + const fs::path root = fs::weakly_canonical(fs::path(base_dir)); + const fs::path target = fs::weakly_canonical(root / path_from_utf8(relative)); + if (!path_prefix_matches(root, target)) return common_failed(403, "forbidden", 403); + return file_response(target); +} + +Response Application::handle_script_client(const Request &request) { + const std::string ip = request.query_value("ip"); + const std::string version = request.query_value("ve").empty() ? "1.0" : request.query_value("ve"); + const auto key = random_key(); + bool cs_mode = false; + std::vector scripts; + const auto projects = projects_for_ip(ip, true); + for (const auto &binding : projects) if (binding.project_name == "cs") cs_mode = true; + auto base = catalog_.base_script_contents(version, cs_mode); + scripts.insert(scripts.end(), base.begin(), base.end()); + for (const auto &binding : projects) { + if (binding.project_name == "cs") continue; + auto extra = catalog_.project_script_contents(binding.project_name, version); + scripts.insert(scripts.end(), extra.begin(), extra.end()); + } + auto custom = catalog_.custom_client_scripts(ip, version); + scripts.insert(scripts.end(), custom.begin(), custom.end()); + std::ostringstream payload; + for (const auto &script : scripts) payload << to_json_bytes(crypto_.makecode(std::vector(script.begin(), script.end()), key)) << "$$$$$"; + std::string key_string; + for (int item : key) key_string.push_back(static_cast('0' + item)); + Json result = Json::object(); + result["getBaseScriptStr"] = payload.str(); + result["key"] = crypto_.rsa_private_encrypt_script(key_string); + Response response; + response.content_type = "text/plain; charset=utf-8"; + response.body = crypto_.gzip_base64(result.dump()); + return response; +} + +Response Application::handle_script_client2(const Request &request) { + const std::string ip = request.query_value("ip"); + const std::string version = request.query_value("ve").empty() ? "1.0" : request.query_value("ve"); + const auto key = random_key(); + Json values = Json::array(); + auto base = catalog_.base_script_contents(version, false); + for (const auto &script : base) values.push_back(to_json_bytes(crypto_.makecode(std::vector(script.begin(), script.end()), key))); + for (const auto &binding : projects_for_ip(ip, true)) { + auto extra = catalog_.project_script_contents(binding.project_name, version); + for (const auto &script : extra) values.push_back(to_json_bytes(crypto_.makecode(std::vector(script.begin(), script.end()), key))); + } + std::string key_string; + for (int item : key) key_string.push_back(static_cast('0' + item)); + Json result = Json::object(); + result["getBaseScriptStr"] = values; + result["key"] = crypto_.rsa_private_encrypt_script(key_string); + return json_response(200, result); +} + +Response Application::handle_script_getzdy(const Request &request) { + const std::string version = request.query_value("ve").empty() ? "1.0" : request.query_value("ve"); + Json result = Json::object(); + Json list = Json::array(); + for (const auto &script : catalog_.custom_client_scripts(request_ip(request), version)) list.push_back(script); + result["getBaseScriptStr"] = list; + Response response; + response.content_type = "application/json; charset=utf-8"; + response.body = result.dump(); + return response; +} + +Response Application::handle_script_getpro(const Request &request) { + const Json body = parse_body_or_empty(request); + const std::string prot = body["prot"].to_string_or(request.query_value("prot")); + const std::string mac = body["mac"].to_string_or(request.query_value("mac")); + const std::string stamp_text = body["l"].is_number() ? std::to_string(body["l"].as_int64()) : body["l"].to_string_or(request.query_value("l")); + const long long stamp = stamp_text.empty() ? 0 : std::stoll(stamp_text); + const std::string s = body["s"].to_string_or(request.query_value("s")); + const std::string ip = request_ip(request); + const long long now = static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + if (std::llabs(now - stamp) > 5LL * 60 * 1000 || prot.empty() || mac.empty()) { + Response response; response.content_type = "text/plain; charset=utf-8"; response.body = ""; return response; + } + { + std::lock_guard lock(script_mutex_); + if (ip_and_mac_.count(ip) == 0) ip_and_mac_[ip] = mac; + } + const auto bindings = projects_for_ip(ip, true); + if (bindings.empty()) { + Response response; response.content_type = "text/plain; charset=utf-8"; response.body = ""; return response; + } + std::ostringstream names; + for (const auto &binding : bindings) names << binding.project_name << ","; + Json result = Json::object(); + result["pro"] = names.str(); + result["validate"] = now; + result["ce"] = s; + result["key"] = make_random_name(); + result["ip"] = ip; + Response response; + response.content_type = "text/plain; charset=utf-8"; + response.body = crypto_.rsa_private_encrypt_tool(result.dump()); + return response; +} + +Response Application::handle_script_getservice(const Request &request) { + const std::string ip = request_ip(request); + const auto key = random_key(); + auto scripts = catalog_.dps_service_scripts(ip); + for (const auto &binding : projects_for_ip(ip, true)) { + const fs::path root = fs::path(config_.paths.file_root) / "dpsScript" / "project" / binding.project_name; + const fs::path config = root / "config"; + if (!fs::exists(config)) continue; + for (const auto &item : split_csv_config(config.string())) { + const std::string path = (root / item).string(); + const std::string content = read_text_file(path); + if (!content.empty()) scripts.emplace_back(path, content); + } + } + std::ostringstream payload; + for (const auto &entry : scripts) { + payload << entry.first << "$$$$$" << to_json_bytes(crypto_.makecode(std::vector(entry.second.begin(), entry.second.end()), key)) << "$$$$$"; + } + std::string key_string; + for (int item : key) key_string.push_back(static_cast('0' + item)); + Json result = Json::object(); + result["getBaseScriptStr"] = payload.str(); + result["key"] = crypto_.rsa_private_encrypt_script(key_string); + Response response; + response.content_type = "text/plain; charset=utf-8"; + response.body = crypto_.gzip_base64(result.dump()); + return response; +} + +Response Application::handle_script_base_update_time(const Request &request) { + Response response; + response.content_type = "text/plain; charset=utf-8"; + response.body = std::to_string(catalog_.base_update_time(request.query_value("ve").empty() ? "1.0" : request.query_value("ve"))); + return response; +} + +Response Application::handle_script_base_sut(const Request &) { return file_response(join_path(config_.paths.file_root, "sut.tar.gz"), "sut.tar.gz"); } + +Response Application::handle_script_getservice_new(const Request &request) { + const std::string version = request.query_value("ve").empty() ? "1.0" : request.query_value("ve"); + const std::string ip = request_ip(request); + const auto bindings = projects_for_ip(ip, true); + if (bindings.empty()) { + Response response; response.content_type = "text/plain; charset=utf-8"; response.body = "没有项目"; return response; + } + const auto key = random_key(); + std::ostringstream payload; + for (const auto &binding : bindings) { + auto scripts = catalog_.project_script_contents(binding.project_name, version); + for (const auto &script : scripts) payload << binding.project_name << "$$$$$" << to_json_bytes(crypto_.makecode(std::vector(script.begin(), script.end()), key)) << "$$$$$"; + } + std::string key_string; + for (int item : key) key_string.push_back(static_cast('0' + item)); + Json result = Json::object(); + result["getBaseScriptStr"] = payload.str(); + result["key"] = crypto_.rsa_private_encrypt_script(key_string); + Response response; + response.content_type = "text/plain; charset=utf-8"; + response.body = crypto_.gzip_base64(result.dump()); + return response; +} + +Response Application::handle_video_placeholder(const Request &) { return common_failed(501, "video service is deferred in v1", 501); } +Response Application::handle_payment_placeholder(const Request &) { return common_failed(501, "payment service is deferred in v1", 501); } + +int Application::run() { + HttpServer server(config_.server.host, config_.server.port, config_.logging, router_); + return server.run(); +} + +} // namespace dps + diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..f5e6617 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,117 @@ +#include "dps/config.hpp" + +#include +#include +#include +#include +#include + +namespace dps { + +namespace { + +std::string trim(const std::string &value) { + std::size_t left = 0; + while (left < value.size() && std::isspace(static_cast(value[left]))) { + ++left; + } + std::size_t right = value.size(); + while (right > left && std::isspace(static_cast(value[right - 1]))) { + --right; + } + return value.substr(left, right - left); +} + +bool parse_bool(const std::string &value) { + return value == "1" || value == "true" || value == "TRUE" || value == "yes" || value == "on"; +} + +std::string to_lower(std::string value) { + for (char &ch : value) { + ch = static_cast(std::tolower(static_cast(ch))); + } + return value; +} + +std::string env_or(const char *name, const std::string &fallback) { +#ifdef _WIN32 + char *raw = nullptr; + std::size_t size = 0; + if (_dupenv_s(&raw, &size, name) != 0 || raw == nullptr) { + return fallback; + } + std::string value(raw); + std::free(raw); + return value; +#else + const char *raw = std::getenv(name); + return raw == nullptr ? fallback : std::string(raw); +#endif +} + +} // namespace + +AppConfig load_config(const std::string &path) { + AppConfig config; + + std::ifstream input(path); + if (!input.good()) { + throw std::runtime_error("failed to open config file: " + path); + } + + std::string line; + while (std::getline(input, line)) { + line = trim(line); + if (line.empty() || line[0] == '#') { + continue; + } + const std::size_t pos = line.find('='); + if (pos == std::string::npos) { + continue; + } + const std::string key = trim(line.substr(0, pos)); + const std::string value = trim(line.substr(pos + 1)); + + if (key == "server.host") config.server.host = value; + else if (key == "server.port") config.server.port = std::stoi(value); + else if (key == "server.trust_proxy") config.server.trust_proxy = parse_bool(value); + else if (key == "server.proxy_header") config.server.proxy_header = value; + else if (key == "jwt.secret") config.jwt.secret = value; + else if (key == "jwt.expiration_seconds") config.jwt.expiration_seconds = std::stoll(value); + else if (key == "logging.level") config.logging.level = value; + else if (key == "logging.http_summary") config.logging.http_summary = parse_bool(value); + else if (key == "logging.http_dump") config.logging.http_dump = parse_bool(value); + else if (key == "logging.http_max_body") config.logging.http_max_body = std::stoi(value); + else if (key == "features.video_enabled") config.features.video_enabled = parse_bool(value); + else if (key == "features.payment_enabled") config.features.payment_enabled = parse_bool(value); + else if (key == "database.host") config.database.host = value; + else if (key == "database.port") config.database.port = std::stoi(value); + else if (key == "database.name") config.database.name = value; + else if (key == "database.user") config.database.user = value; + else if (key == "database.password") config.database.password = value; + else if (key == "database.ssl_mode") config.database.ssl_mode = to_lower(value); + else if (key == "database.ssl_ca") config.database.ssl_ca = value; + else if (key == "database.plugin_dir") config.database.plugin_dir = value; + else if (key == "paths.configfile") config.paths.configfile = value; + else if (key == "paths.file_root") config.paths.file_root = value; + else if (key == "paths.file_map") config.paths.file_map = value; + else if (key == "paths.script_root") config.paths.script_root = value; + else if (key == "paths.dps_root") config.paths.dps_root = value; + else if (key == "paths.client_user_script_root") config.paths.client_user_script_root = value; + } + + config.server.host = env_or("DPS_SERVER_HOST", config.server.host); + config.server.proxy_header = env_or("DPS_SERVER_PROXY_HEADER", config.server.proxy_header); + config.jwt.secret = env_or("DPS_JWT_SECRET", config.jwt.secret); + config.database.host = env_or("DPS_DB_HOST", config.database.host); + config.database.user = env_or("DPS_DB_USER", config.database.user); + config.database.password = env_or("DPS_DB_PASSWORD", config.database.password); + config.database.name = env_or("DPS_DB_NAME", config.database.name); + config.database.ssl_mode = to_lower(env_or("DPS_DB_SSL_MODE", config.database.ssl_mode)); + config.database.ssl_ca = env_or("DPS_DB_SSL_CA", config.database.ssl_ca); + config.database.plugin_dir = env_or("DPS_DB_PLUGIN_DIR", config.database.plugin_dir); + + return config; +} + +} // namespace dps diff --git a/src/fs_utils.cpp b/src/fs_utils.cpp new file mode 100644 index 0000000..3a45ceb --- /dev/null +++ b/src/fs_utils.cpp @@ -0,0 +1,33 @@ +#include "dps/fs_utils.hpp" + +#include +#include +#include + +namespace fs = std::filesystem; + +namespace dps { + +fs::path path_from_utf8(const std::string &value) { + return fs::u8path(value); +} + +std::string path_to_utf8_string(const fs::path &path) { + return path.u8string(); +} + +std::string read_text_file(const fs::path &path) { + std::ifstream input(path, std::ios::binary); + if (!input.good()) return ""; + std::ostringstream buffer; + buffer << input.rdbuf(); + return buffer.str(); +} + +std::vector read_binary_file(const fs::path &path) { + std::ifstream input(path, std::ios::binary); + if (!input.good()) return {}; + return std::vector((std::istreambuf_iterator(input)), std::istreambuf_iterator()); +} + +} // namespace dps diff --git a/src/http.cpp b/src/http.cpp new file mode 100644 index 0000000..3076a79 --- /dev/null +++ b/src/http.cpp @@ -0,0 +1,534 @@ +#include "dps/http.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +using socket_t = SOCKET; +static constexpr socket_t kInvalidSocket = INVALID_SOCKET; +#else +#include +#include +#include +#include +using socket_t = int; +static constexpr socket_t kInvalidSocket = -1; +#endif + +namespace dps { + +namespace { + +std::string to_lower(std::string value) { + std::transform(value.begin(), value.end(), value.begin(), [](unsigned char ch) { + return static_cast(std::tolower(ch)); + }); + return value; +} + +std::string trim(const std::string &value) { + std::size_t left = 0; + while (left < value.size() && std::isspace(static_cast(value[left]))) { + ++left; + } + std::size_t right = value.size(); + while (right > left && std::isspace(static_cast(value[right - 1]))) { + --right; + } + return value.substr(left, right - left); +} + +std::string now_text() { + const auto now = std::chrono::system_clock::now(); + const std::time_t time = std::chrono::system_clock::to_time_t(now); + std::tm local{}; +#ifdef _WIN32 + localtime_s(&local, &time); +#else + localtime_r(&time, &local); +#endif + std::ostringstream out; + out << std::put_time(&local, "%Y-%m-%d %H:%M:%S"); + return out.str(); +} + +bool is_text_content_type(const std::string &content_type) { + const std::string lower = to_lower(content_type); + return lower.find("json") != std::string::npos || lower.find("text/") != std::string::npos || + lower.find("x-www-form-urlencoded") != std::string::npos || lower.find("xml") != std::string::npos; +} + +bool is_mostly_printable(const std::string &text) { + if (text.empty()) { + return true; + } + std::size_t printable = 0; + for (unsigned char ch : text) { + if (std::isprint(ch) || std::isspace(ch)) { + ++printable; + } + } + return printable * 100 / text.size() >= 90; +} + +std::string truncate_text(const std::string &text, int max_body) { + if (max_body <= 0 || text.size() <= static_cast(max_body)) { + return text; + } + return text.substr(0, static_cast(max_body)) + "..."; +} + +std::string request_header_summary(const Request &request) { + std::ostringstream out; + const std::string content_type = request.header("content-type"); + out << "content_type=" << (content_type.empty() ? "-" : content_type); + out << " auth=" << (request.header("authorization").empty() ? "absent" : "present"); + return out.str(); +} + +void log_request_summary(const LoggingConfig &logging, const Request &request) { + if (!logging.http_summary) { + return; + } + std::clog << "[" << now_text() << "] REQ ip=" << request.remote_addr + << " method=" << request.method + << " path=" << request.path + << " query=" << (request.query.empty() ? "-" : request.raw_target.substr(request.path.size())) + << " body_bytes=" << request.body.size() + << " " << request_header_summary(request) + << std::endl; + if (logging.http_dump && !request.body.empty()) { + const std::string content_type = request.header("content-type"); + if ((content_type.empty() || is_text_content_type(content_type)) && is_mostly_printable(request.body)) { + std::clog << "[" << now_text() << "] REQ_BODY " + << truncate_text(request.body, logging.http_max_body) + << std::endl; + } + } +} + +void log_response_summary(const LoggingConfig &logging, const Request &request, const Response &response, long long cost_ms) { + if (!logging.http_summary) { + return; + } + const std::size_t body_bytes = response.has_binary() ? response.binary_body.size() : response.body.size(); + std::clog << "[" << now_text() << "] RESP ip=" << request.remote_addr + << " method=" << request.method + << " path=" << request.path + << " status=" << response.status + << " content_type=" << response.content_type + << " body_bytes=" << body_bytes + << " binary=" << (response.has_binary() ? "yes" : "no") + << " cost_ms=" << cost_ms + << std::endl; + if (logging.http_dump && !response.has_binary() && !response.body.empty() && + is_text_content_type(response.content_type) && is_mostly_printable(response.body)) { + std::clog << "[" << now_text() << "] RESP_BODY " + << truncate_text(response.body, logging.http_max_body) + << std::endl; + } +} + +std::vector split_path(const std::string &path) { + std::vector result; + std::string current; + for (char ch : path) { + if (ch == '/') { + if (!current.empty()) { + result.push_back(current); + current.clear(); + } + continue; + } + current.push_back(ch); + } + if (!current.empty()) { + result.push_back(current); + } + return result; +} + +bool match_route(const std::string &pattern, const std::string &path, std::map *params) { + const auto lhs = split_path(pattern); + const auto rhs = split_path(path); + if (lhs.size() != rhs.size()) { + return false; + } + for (std::size_t i = 0; i < lhs.size(); ++i) { + const std::string &expected = lhs[i]; + const std::string actual = url_decode(rhs[i]); + if (expected.size() >= 2 && expected.front() == '{' && expected.back() == '}') { + (*params)[expected.substr(1, expected.size() - 2)] = actual; + continue; + } + if (expected != actual) { + return false; + } + } + return true; +} + +void close_socket(socket_t sock) { +#ifdef _WIN32 + closesocket(sock); +#else + close(sock); +#endif +} + +bool parse_ipv4_address(const std::string &host, in_addr *addr) { + if (host.empty() || host == "0.0.0.0") { + addr->s_addr = htonl(INADDR_ANY); + return true; + } +#ifdef _WIN32 + return InetPtonA(AF_INET, host.c_str(), addr) == 1; +#else + return inet_pton(AF_INET, host.c_str(), addr) == 1; +#endif +} + +std::string ipv4_address_to_string(const in_addr &addr) { + char buffer[INET_ADDRSTRLEN] = {}; +#ifdef _WIN32 + if (InetNtopA(AF_INET, const_cast(&addr), buffer, static_cast(sizeof(buffer))) == nullptr) { + return ""; + } +#else + if (inet_ntop(AF_INET, &addr, buffer, sizeof(buffer)) == nullptr) { + return ""; + } +#endif + return buffer; +} + +bool send_all(socket_t sock, const unsigned char *data, std::size_t size) { + std::size_t sent = 0; + while (sent < size) { +#ifdef _WIN32 + const int count = send(sock, reinterpret_cast(data + sent), static_cast(size - sent), 0); +#else + const auto count = send(sock, data + sent, size - sent, 0); +#endif + if (count <= 0) { + return false; + } + sent += static_cast(count); + } + return true; +} + +std::string reason_phrase(int status) { + switch (status) { + case 200: return "OK"; + case 201: return "Created"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + default: return "OK"; + } +} + +std::string make_http_message(const Response &response) { + std::ostringstream out; + const std::size_t content_length = response.has_binary() ? response.binary_body.size() : response.body.size(); + out << "HTTP/1.1 " << response.status << " " << reason_phrase(response.status) << "\r\n"; + out << "Connection: close\r\n"; + out << "Content-Length: " << content_length << "\r\n"; + out << "Content-Type: " << response.content_type << "\r\n"; + for (const auto &header : response.headers) { + out << header.first << ": " << header.second << "\r\n"; + } + out << "\r\n"; + return out.str(); +} + +Response bad_request(const std::string &message) { + Response response; + response.status = 400; + response.body = "{\"code\":400,\"message\":\"" + message + "\",\"data\":null}"; + return response; +} + +std::string read_request(socket_t client) { + std::string data; + std::array buffer{}; + std::size_t header_end = std::string::npos; + std::size_t content_length = 0; + while (true) { +#ifdef _WIN32 + const int bytes = recv(client, buffer.data(), static_cast(buffer.size()), 0); +#else + const auto bytes = recv(client, buffer.data(), buffer.size(), 0); +#endif + if (bytes <= 0) { + break; + } + data.append(buffer.data(), static_cast(bytes)); + if (header_end == std::string::npos) { + header_end = data.find("\r\n\r\n"); + if (header_end != std::string::npos) { + const std::string headers = data.substr(0, header_end + 4); + const std::size_t pos = to_lower(headers).find("content-length:"); + if (pos != std::string::npos) { + std::size_t value_start = headers.find(':', pos); + std::size_t value_end = headers.find("\r\n", value_start); + content_length = static_cast( + std::stoll(trim(headers.substr(value_start + 1, value_end - value_start - 1)))); + } + } + } + if (header_end != std::string::npos && data.size() >= header_end + 4 + content_length) { + break; + } + } + return data; +} + +Request parse_request(const std::string &raw, const std::string &remote_addr) { + const std::size_t header_end = raw.find("\r\n\r\n"); + if (header_end == std::string::npos) { + throw std::runtime_error("invalid request"); + } + std::istringstream lines(raw.substr(0, header_end)); + std::string line; + if (!std::getline(lines, line)) { + throw std::runtime_error("missing request line"); + } + if (!line.empty() && line.back() == '\r') { + line.pop_back(); + } + std::istringstream request_line(line); + Request request; + std::string version; + request_line >> request.method >> request.raw_target >> version; + request.remote_addr = remote_addr; + const std::size_t query_pos = request.raw_target.find('?'); + request.path = query_pos == std::string::npos ? request.raw_target : request.raw_target.substr(0, query_pos); + if (query_pos != std::string::npos) { + request.query = parse_query_string(request.raw_target.substr(query_pos + 1)); + } + + while (std::getline(lines, line)) { + if (!line.empty() && line.back() == '\r') { + line.pop_back(); + } + const std::size_t pos = line.find(':'); + if (pos == std::string::npos) { + continue; + } + request.headers[to_lower(trim(line.substr(0, pos)))] = trim(line.substr(pos + 1)); + } + request.body = raw.substr(header_end + 4); + return request; +} + +} // namespace + +std::string Request::header(const std::string &name) const { + const auto it = headers.find(to_lower(name)); + return it == headers.end() ? "" : it->second; +} + +std::string Request::query_value(const std::string &name) const { + const auto it = query.find(name); + return it == query.end() ? "" : it->second; +} + +std::string Request::path_param(const std::string &name) const { + const auto it = path_params.find(name); + return it == path_params.end() ? "" : it->second; +} + +bool Response::has_binary() const { + return !binary_body.empty(); +} + +void Router::add(const std::string &method, const std::string &pattern, bool requires_auth, Handler handler) { + routes_.push_back(Route{to_lower(method), pattern, requires_auth, std::move(handler)}); +} + +Response Router::dispatch(Request request) const { + const std::string method = to_lower(request.method); + for (const auto &route : routes_) { + if (route.method != method) { + continue; + } + std::map params; + if (!match_route(route.pattern, request.path, ¶ms)) { + continue; + } + request.path_params = std::move(params); + return route.handler(request); + } + Response response; + response.status = 404; + response.body = "{\"code\":404,\"message\":\"not found\",\"data\":null}"; + return response; +} + +HttpServer::HttpServer(std::string host, int port, LoggingConfig logging, const Router &router) + : host_(std::move(host)), port_(port), logging_(std::move(logging)), router_(router) {} + +int HttpServer::run() const { +#ifdef _WIN32 + WSADATA wsa_data{}; + if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) { + std::cerr << "WSAStartup failed\n"; + return 1; + } +#endif + + socket_t server = socket(AF_INET, SOCK_STREAM, 0); + if (server == kInvalidSocket) { + std::cerr << "socket creation failed\n"; + return 1; + } + + int reuse = 1; +#ifdef _WIN32 + setsockopt(server, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&reuse), sizeof(reuse)); +#else + setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); +#endif + + sockaddr_in addr{}; + addr.sin_family = AF_INET; + addr.sin_port = htons(static_cast(port_)); + if (!parse_ipv4_address(host_, &addr.sin_addr)) { + std::cerr << "invalid listen host: " << host_ << "\n"; + close_socket(server); + return 1; + } + + if (bind(server, reinterpret_cast(&addr), sizeof(addr)) != 0) { + std::cerr << "bind failed\n"; + close_socket(server); + return 1; + } + if (listen(server, 64) != 0) { + std::cerr << "listen failed\n"; + close_socket(server); + return 1; + } + + std::clog << "Listening on " << host_ << ":" << port_ << std::endl; + while (true) { + sockaddr_in client_addr{}; +#ifdef _WIN32 + int client_len = sizeof(client_addr); +#else + socklen_t client_len = sizeof(client_addr); +#endif + socket_t client = accept(server, reinterpret_cast(&client_addr), &client_len); + if (client == kInvalidSocket) { + continue; + } + std::thread([this, client, client_addr]() { + const std::string remote = ipv4_address_to_string(client_addr.sin_addr); + try { + const std::string raw = read_request(client); + if (raw.empty()) { + close_socket(client); + return; + } + Request request = parse_request(raw, remote); + log_request_summary(logging_, request); + const auto started = std::chrono::steady_clock::now(); + const Response response = router_.dispatch(request); + const auto finished = std::chrono::steady_clock::now(); + const auto cost_ms = std::chrono::duration_cast(finished - started).count(); + log_response_summary(logging_, request, response, cost_ms); + const std::string headers = make_http_message(response); + send_all(client, reinterpret_cast(headers.data()), headers.size()); + if (response.has_binary()) { + send_all(client, response.binary_body.data(), response.binary_body.size()); + } else { + send_all(client, reinterpret_cast(response.body.data()), response.body.size()); + } + } catch (const std::exception &ex) { + const Response response = bad_request(ex.what()); + Request fallback; + fallback.remote_addr = remote; + fallback.method = "UNKNOWN"; + fallback.path = "-"; + log_response_summary(logging_, fallback, response, 0); + const std::string headers = make_http_message(response); + send_all(client, reinterpret_cast(headers.data()), headers.size()); + send_all(client, reinterpret_cast(response.body.data()), response.body.size()); + } + close_socket(client); + }).detach(); + } + + return 0; +} + +std::string url_decode(const std::string &value) { + std::ostringstream out; + for (std::size_t i = 0; i < value.size(); ++i) { + const char ch = value[i]; + if (ch == '+' ) { + out << ' '; + } else if (ch == '%' && i + 2 < value.size()) { + const std::string hex = value.substr(i + 1, 2); + out << static_cast(std::strtol(hex.c_str(), nullptr, 16)); + i += 2; + } else { + out << ch; + } + } + return out.str(); +} + +std::map parse_query_string(const std::string &query) { + std::map values; + std::size_t start = 0; + while (start < query.size()) { + const std::size_t amp = query.find('&', start); + const std::string token = query.substr(start, amp == std::string::npos ? std::string::npos : amp - start); + const std::size_t eq = token.find('='); + if (eq == std::string::npos) { + values[url_decode(token)] = ""; + } else { + values[url_decode(token.substr(0, eq))] = url_decode(token.substr(eq + 1)); + } + if (amp == std::string::npos) { + break; + } + start = amp + 1; + } + return values; +} + +std::string guess_content_type(const std::string &path) { + const auto lower = to_lower(path); + if (lower.size() >= 4 && lower.substr(lower.size() - 4) == ".png") return "image/png"; + if (lower.size() >= 4 && lower.substr(lower.size() - 4) == ".jpg") return "image/jpeg"; + if (lower.size() >= 5 && lower.substr(lower.size() - 5) == ".jpeg") return "image/jpeg"; + if (lower.size() >= 4 && lower.substr(lower.size() - 4) == ".mp4") return "video/mp4"; + if (lower.size() >= 4 && lower.substr(lower.size() - 4) == ".zip") return "application/zip"; + if (lower.size() >= 7 && lower.substr(lower.size() - 7) == ".tar.gz") return "application/gzip"; + if (lower.size() >= 4 && lower.substr(lower.size() - 4) == ".txt") return "text/plain; charset=utf-8"; + if (lower.size() >= 5 && lower.substr(lower.size() - 5) == ".json") return "application/json; charset=utf-8"; + if (lower.size() >= 3 && lower.substr(lower.size() - 3) == ".sh") return "application/octet-stream"; + return "application/octet-stream"; +} + +} // namespace dps diff --git a/src/json.cpp b/src/json.cpp new file mode 100644 index 0000000..ab0f796 --- /dev/null +++ b/src/json.cpp @@ -0,0 +1,384 @@ +#include "dps/json.hpp" + +#include +#include +#include + +namespace dps { + +namespace { + +class Parser { +public: + explicit Parser(const std::string &text) : text_(text) {} + + Json parse() { + skip_ws(); + Json value = parse_value(); + skip_ws(); + if (pos_ != text_.size()) { + throw std::runtime_error("unexpected trailing json content"); + } + return value; + } + +private: + Json parse_value() { + if (match("null")) { + return Json(); + } + if (match("true")) { + return Json(true); + } + if (match("false")) { + return Json(false); + } + if (peek() == '"') { + return Json(parse_string()); + } + if (peek() == '[') { + return parse_array(); + } + if (peek() == '{') { + return parse_object(); + } + return Json(parse_number()); + } + + Json parse_array() { + consume('['); + Json::array_t items; + skip_ws(); + if (peek() == ']') { + consume(']'); + return Json(std::move(items)); + } + while (true) { + skip_ws(); + items.push_back(parse_value()); + skip_ws(); + if (peek() == ']') { + consume(']'); + break; + } + consume(','); + } + return Json(std::move(items)); + } + + Json parse_object() { + consume('{'); + Json::object_t object; + skip_ws(); + if (peek() == '}') { + consume('}'); + return Json(std::move(object)); + } + while (true) { + skip_ws(); + std::string key = parse_string(); + skip_ws(); + consume(':'); + skip_ws(); + object.emplace(std::move(key), parse_value()); + skip_ws(); + if (peek() == '}') { + consume('}'); + break; + } + consume(','); + } + return Json(std::move(object)); + } + + std::string parse_string() { + consume('"'); + std::ostringstream out; + while (pos_ < text_.size()) { + char ch = text_[pos_++]; + if (ch == '"') { + return out.str(); + } + if (ch != '\\') { + out << ch; + continue; + } + if (pos_ >= text_.size()) { + throw std::runtime_error("unterminated escape"); + } + char esc = text_[pos_++]; + switch (esc) { + case '"': out << '"'; break; + case '\\': out << '\\'; break; + case '/': out << '/'; break; + case 'b': out << '\b'; break; + case 'f': out << '\f'; break; + case 'n': out << '\n'; break; + case 'r': out << '\r'; break; + case 't': out << '\t'; break; + case 'u': { + if (pos_ + 4 > text_.size()) { + throw std::runtime_error("bad unicode escape"); + } + const std::string hex = text_.substr(pos_, 4); + pos_ += 4; + const unsigned int code = static_cast(std::stoul(hex, nullptr, 16)); + if (code <= 0x7F) { + out << static_cast(code); + } else if (code <= 0x7FF) { + out << static_cast(0xC0 | ((code >> 6) & 0x1F)); + out << static_cast(0x80 | (code & 0x3F)); + } else { + out << static_cast(0xE0 | ((code >> 12) & 0x0F)); + out << static_cast(0x80 | ((code >> 6) & 0x3F)); + out << static_cast(0x80 | (code & 0x3F)); + } + break; + } + default: + throw std::runtime_error("unsupported escape"); + } + } + throw std::runtime_error("unterminated string"); + } + + double parse_number() { + const std::size_t start = pos_; + if (peek() == '-') { + ++pos_; + } + while (std::isdigit(static_cast(peek()))) { + ++pos_; + } + if (peek() == '.') { + ++pos_; + while (std::isdigit(static_cast(peek()))) { + ++pos_; + } + } + if (peek() == 'e' || peek() == 'E') { + ++pos_; + if (peek() == '+' || peek() == '-') { + ++pos_; + } + while (std::isdigit(static_cast(peek()))) { + ++pos_; + } + } + return std::stod(text_.substr(start, pos_ - start)); + } + + char peek() const { + return pos_ < text_.size() ? text_[pos_] : '\0'; + } + + void consume(char expected) { + if (peek() != expected) { + throw std::runtime_error("unexpected token"); + } + ++pos_; + } + + bool match(const char *literal) { + const std::size_t len = std::char_traits::length(literal); + if (text_.compare(pos_, len, literal) == 0) { + pos_ += len; + return true; + } + return false; + } + + void skip_ws() { + while (pos_ < text_.size() && std::isspace(static_cast(text_[pos_]))) { + ++pos_; + } + } + + const std::string &text_; + std::size_t pos_ = 0; +}; + +std::string escape_string(const std::string &value) { + std::ostringstream out; + for (const unsigned char ch : value) { + switch (ch) { + case '"': out << "\\\""; break; + case '\\': out << "\\\\"; break; + case '\b': out << "\\b"; break; + case '\f': out << "\\f"; break; + case '\n': out << "\\n"; break; + case '\r': out << "\\r"; break; + case '\t': out << "\\t"; break; + default: + if (ch < 0x20) { + out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast(ch); + } else { + out << static_cast(ch); + } + } + } + return out.str(); +} + +} // namespace + +Json::Json() : value_(nullptr) {} +Json::Json(std::nullptr_t) : value_(nullptr) {} +Json::Json(bool value) : value_(value) {} +Json::Json(int value) : value_(static_cast(value)) {} +Json::Json(long long value) : value_(static_cast(value)) {} +Json::Json(double value) : value_(value) {} +Json::Json(const char *value) : value_(std::string(value == nullptr ? "" : value)) {} +Json::Json(const std::string &value) : value_(value) {} +Json::Json(std::string &&value) : value_(std::move(value)) {} +Json::Json(const array_t &value) : value_(value) {} +Json::Json(array_t &&value) : value_(std::move(value)) {} +Json::Json(const object_t &value) : value_(value) {} +Json::Json(object_t &&value) : value_(std::move(value)) {} + +Json Json::parse(const std::string &text) { + return Parser(text).parse(); +} + +Json Json::object() { + return Json(object_t{}); +} + +Json Json::array() { + return Json(array_t{}); +} + +bool Json::is_null() const { return std::holds_alternative(value_); } +bool Json::is_bool() const { return std::holds_alternative(value_); } +bool Json::is_number() const { return std::holds_alternative(value_); } +bool Json::is_string() const { return std::holds_alternative(value_); } +bool Json::is_array() const { return std::holds_alternative(value_); } +bool Json::is_object() const { return std::holds_alternative(value_); } + +bool Json::as_bool() const { return std::get(value_); } +double Json::as_number() const { return std::get(value_); } +int Json::as_int() const { return static_cast(as_number()); } +long long Json::as_int64() const { return static_cast(as_number()); } +const std::string &Json::as_string() const { return std::get(value_); } +const Json::array_t &Json::as_array() const { return std::get(value_); } +const Json::object_t &Json::as_object() const { return std::get(value_); } +Json::array_t &Json::as_array() { return std::get(value_); } +Json::object_t &Json::as_object() { return std::get(value_); } + +bool Json::contains(const std::string &key) const { + if (!is_object()) { + return false; + } + return as_object().find(key) != as_object().end(); +} + +const Json &Json::at(const std::string &key) const { + return as_object().at(key); +} + +Json &Json::operator[](const std::string &key) { + if (!is_object()) { + value_ = object_t{}; + } + return as_object()[key]; +} + +const Json &Json::operator[](const std::string &key) const { + static const Json kNull; + if (!is_object()) { + return kNull; + } + const auto it = as_object().find(key); + return it == as_object().end() ? kNull : it->second; +} + +const Json &Json::at(std::size_t index) const { + return as_array().at(index); +} + +Json &Json::at(std::size_t index) { + return as_array().at(index); +} + +void Json::push_back(const Json &value) { + if (!is_array()) { + value_ = array_t{}; + } + as_array().push_back(value); +} + +std::size_t Json::size() const { + if (is_array()) { + return as_array().size(); + } + if (is_object()) { + return as_object().size(); + } + return 0; +} + +std::string Json::dump() const { + if (is_null()) { + return "null"; + } + if (is_bool()) { + return as_bool() ? "true" : "false"; + } + if (is_number()) { + std::ostringstream out; + out << std::setprecision(15) << as_number(); + std::string result = out.str(); + if (result.find('.') != std::string::npos) { + while (!result.empty() && result.back() == '0') { + result.pop_back(); + } + if (!result.empty() && result.back() == '.') { + result.pop_back(); + } + } + return result.empty() ? "0" : result; + } + if (is_string()) { + return "\"" + escape_string(as_string()) + "\""; + } + if (is_array()) { + std::ostringstream out; + out << "["; + bool first = true; + for (const auto &item : as_array()) { + if (!first) { + out << ","; + } + first = false; + out << item.dump(); + } + out << "]"; + return out.str(); + } + std::ostringstream out; + out << "{"; + bool first = true; + for (const auto &entry : as_object()) { + if (!first) { + out << ","; + } + first = false; + out << "\"" << escape_string(entry.first) << "\":" << entry.second.dump(); + } + out << "}"; + return out.str(); +} + +std::string Json::to_string_or(const std::string &fallback) const { + return is_string() ? as_string() : fallback; +} + +int Json::to_int_or(int fallback) const { + return is_number() ? as_int() : fallback; +} + +bool Json::to_bool_or(bool fallback) const { + return is_bool() ? as_bool() : fallback; +} + +} // namespace dps diff --git a/src/labels.cpp b/src/labels.cpp new file mode 100644 index 0000000..26863ba --- /dev/null +++ b/src/labels.cpp @@ -0,0 +1,17 @@ +#include "dps/labels.hpp" + +namespace dps { + +std::string membership_label(int level) { + switch (level) { + case 2: + return "\xE9\x92\xBB\xE7\x9F\xB3\xE4\xBC\x9A\xE5\x91\x98"; + case 3: + return "\xE8\x87\xB3\xE8\x87\xBB\xE4\xBC\x9A\xE5\x91\x98"; + case 1: + default: + return "\xE9\xBB\x84\xE9\x87\x91\xE4\xBC\x9A\xE5\x91\x98"; + } +} + +} // namespace dps diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..4f8369d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,25 @@ +#include "dps/app.hpp" +#include "dps/config.hpp" + +#include +#include +#include + +int main(int argc, char **argv) { + std::string config_path = "config/server.conf"; + for (int i = 1; i < argc; ++i) { + const std::string arg = argv[i]; + if (arg == "--config" && i + 1 < argc) { + config_path = argv[++i]; + } + } + + try { + dps::AppConfig config = dps::load_config(config_path); + dps::Application app(std::move(config)); + return app.run(); + } catch (const std::exception &ex) { + std::cerr << "failed to start dps_manager_server: " << ex.what() << "\n"; + return 1; + } +} diff --git a/tests/config_sync_test.py b/tests/config_sync_test.py new file mode 100644 index 0000000..3325a30 --- /dev/null +++ b/tests/config_sync_test.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import difflib +import sys +from pathlib import Path + + +def normalize(text: str) -> str: + return text.replace("\r\n", "\n").strip() + "\n" + + +def assert_same_file(expected: Path, actual: Path) -> None: + expected_text = normalize(expected.read_text(encoding="utf-8")) + actual_text = normalize(actual.read_text(encoding="utf-8")) + if expected_text == actual_text: + return + + diff = "".join( + difflib.unified_diff( + expected_text.splitlines(keepends=True), + actual_text.splitlines(keepends=True), + fromfile=str(expected), + tofile=str(actual), + ) + ) + raise AssertionError(diff) + + +def main() -> int: + project_root = Path(__file__).resolve().parents[1] + source_config = project_root / "config" / "server.conf" + build_configs = [ + project_root / "build" / "windows" / "x64" / "debug" / "config" / "server.conf", + project_root / "build" / "windows" / "x64" / "release" / "config" / "server.conf", + ] + + missing = [path for path in build_configs if not path.is_file()] + if missing: + raise AssertionError( + "missing build config(s): " + ", ".join(str(path) for path in missing) + ) + + for build_config in build_configs: + assert_same_file(source_config, build_config) + + print("config sync test passed") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/crypto_smoke_test.cpp b/tests/crypto_smoke_test.cpp new file mode 100644 index 0000000..44c2471 --- /dev/null +++ b/tests/crypto_smoke_test.cpp @@ -0,0 +1,162 @@ +#include "dps/app.hpp" +#include "dps/config.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace fs = std::filesystem; + +namespace { + +void set_env(const char *name, const char *value) { +#ifdef _WIN32 + _putenv_s(name, value); +#else + setenv(name, value, 1); +#endif +} + +void clear_env(const char *name) { +#ifdef _WIN32 + _putenv_s(name, ""); +#else + unsetenv(name); +#endif +} + +std::vector decode_base64(const std::string &text) { + std::string normalized = text; + while (normalized.size() % 4 != 0) normalized.push_back('='); + + std::vector decoded((normalized.size() / 4) * 3); + const int size = EVP_DecodeBlock(decoded.data(), + reinterpret_cast(normalized.data()), + static_cast(normalized.size())); + assert(size >= 0); + + std::size_t padding = 0; + if (!normalized.empty() && normalized.back() == '=') ++padding; + if (normalized.size() > 1 && normalized[normalized.size() - 2] == '=') ++padding; + decoded.resize(static_cast(size) - padding); + return decoded; +} + +std::string gunzip_bytes(const std::vector &compressed) { + z_stream stream{}; + stream.next_in = const_cast(reinterpret_cast(compressed.data())); + stream.avail_in = static_cast(compressed.size()); + + const int init = inflateInit2(&stream, 15 + 16); + assert(init == Z_OK); + + std::string output; + std::vector buffer(4096); + int ret = Z_OK; + while (ret == Z_OK) { + stream.next_out = reinterpret_cast(buffer.data()); + stream.avail_out = static_cast(buffer.size()); + ret = inflate(&stream, Z_NO_FLUSH); + output.append(buffer.data(), buffer.size() - stream.avail_out); + } + inflateEnd(&stream); + assert(ret == Z_STREAM_END); + return output; +} + +void test_crypto() { + dps::LegacyCrypto crypto("unit-test-secret"); + + const std::string password = "P@ssw0rd!"; + const std::string hashed = crypto.hash_password(password); + assert(!hashed.empty()); + assert(hashed.rfind("$2", 0) == 0); + assert(crypto.verify_password(password, hashed)); + assert(!crypto.verify_password(password, password)); + + const std::string token = crypto.issue_jwt("tester", 60); + assert(!token.empty()); + const auto subject = crypto.validate_jwt(token); + assert(subject.has_value()); + assert(subject.value() == "tester"); + assert(!crypto.validate_jwt(token + "broken").has_value()); + + const std::string gzip_payload = crypto.gzip_base64("{\"hello\":\"world\"}"); + assert(!gzip_payload.empty()); + const std::string inflated = gunzip_bytes(decode_base64(gzip_payload)); + assert(inflated == "{\"hello\":\"world\"}"); + + const std::string rsa_tool = crypto.rsa_private_encrypt_tool("abc123"); + const std::string rsa_script = crypto.rsa_private_encrypt_script("xyz789"); + assert(!rsa_tool.empty()); + assert(!rsa_script.empty()); + assert(rsa_tool != "abc123"); + assert(rsa_script != "xyz789"); +} + +void test_config() { + const fs::path path = fs::temp_directory_path() / "dps-config-smoke.conf"; + { + std::ofstream out(path); + out << "server.host=0.0.0.0\n"; + out << "server.port=8651\n"; + out << "server.trust_proxy=true\n"; + out << "server.proxy_header=x-real-ip\n"; + out << "database.host=127.0.0.1\n"; + out << "database.name=rindro\n"; + out << "database.user=root\n"; + out << "database.password=test\n"; + out << "database.ssl_mode=verify_ca\n"; + out << "database.ssl_ca=C:/certs/demo-ca.pem\n"; + out << "database.plugin_dir=C:/mariadb/plugin\n"; + } + + set_env("DPS_SERVER_HOST", "10.0.0.8"); + set_env("DPS_SERVER_PROXY_HEADER", "x-forwarded-for"); + set_env("DPS_DB_HOST", "192.168.0.10"); + set_env("DPS_DB_USER", "tester"); + set_env("DPS_DB_PASSWORD", "secret"); + set_env("DPS_DB_NAME", "demo"); + set_env("DPS_DB_SSL_MODE", "disable"); + set_env("DPS_DB_SSL_CA", "D:/certs/dev-ca.pem"); + set_env("DPS_DB_PLUGIN_DIR", "D:/mariadb/plugin"); + + const dps::AppConfig config = dps::load_config(path.string()); + assert(config.server.host == "10.0.0.8"); + assert(config.server.port == 8651); + assert(config.server.trust_proxy); + assert(config.server.proxy_header == "x-forwarded-for"); + assert(config.database.host == "192.168.0.10"); + assert(config.database.user == "tester"); + assert(config.database.password == "secret"); + assert(config.database.name == "demo"); + assert(config.database.ssl_mode == "disable"); + assert(config.database.ssl_ca == "D:/certs/dev-ca.pem"); + assert(config.database.plugin_dir == "D:/mariadb/plugin"); + + clear_env("DPS_SERVER_HOST"); + clear_env("DPS_SERVER_PROXY_HEADER"); + clear_env("DPS_DB_HOST"); + clear_env("DPS_DB_USER"); + clear_env("DPS_DB_PASSWORD"); + clear_env("DPS_DB_NAME"); + clear_env("DPS_DB_SSL_MODE"); + clear_env("DPS_DB_SSL_CA"); + clear_env("DPS_DB_PLUGIN_DIR"); + std::remove(path.string().c_str()); +} + +} // namespace + +int main() { + test_crypto(); + test_config(); + return 0; +} diff --git a/tests/fs_utils_smoke_test.cpp b/tests/fs_utils_smoke_test.cpp new file mode 100644 index 0000000..04c0a2b --- /dev/null +++ b/tests/fs_utils_smoke_test.cpp @@ -0,0 +1,44 @@ +#include "dps/fs_utils.hpp" + +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +namespace { + +std::string utf8_text() { + return "2_\xE5\x87\x8C\xE4\xBC\x97"; +} + +int run_test() { + const fs::path root = fs::temp_directory_path() / "dps-fs-utils-smoke"; + const fs::path nested = root / dps::path_from_utf8(utf8_text()); + const fs::path file = nested / "img.png"; + + fs::remove_all(root); + fs::create_directories(nested); + + { + std::ofstream output(file, std::ios::binary); + output << "png-data"; + } + + const std::vector bytes = dps::read_binary_file(file); + if (bytes.size() != 8) { + std::cerr << "unexpected file size: " << bytes.size() << "\n"; + return EXIT_FAILURE; + } + + fs::remove_all(root); + return EXIT_SUCCESS; +} + +} // namespace + +int main() { + return run_test(); +} diff --git a/tests/get_info_level_contract_test.py b/tests/get_info_level_contract_test.py new file mode 100644 index 0000000..1afe4b1 --- /dev/null +++ b/tests/get_info_level_contract_test.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +import re +import sys +from pathlib import Path + + +def main() -> int: + source = Path(__file__).resolve().parents[1] / "src" / "app.cpp" + text = source.read_text(encoding="utf-8-sig") + + match = re.search( + r"Response Application::handle_get_info\(const Request &request\) \{(?P.*?)\n\}", + text, + re.S, + ) + if not match: + raise AssertionError("handle_get_info definition not found") + + body = match.group("body") + if 'body["level"] = user->level;' not in body: + raise AssertionError('missing `body["level"] = user->level;` in handle_get_info') + if 'body["uservip"] = membership_label(user->level);' not in body: + raise AssertionError('missing dynamic `uservip` mapping in handle_get_info') + + print("get_info level contract test passed") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/json_smoke_test.cpp b/tests/json_smoke_test.cpp new file mode 100644 index 0000000..a1b9f13 --- /dev/null +++ b/tests/json_smoke_test.cpp @@ -0,0 +1,18 @@ +#include "dps/json.hpp" + +#include +#include + +int main() { + const dps::Json value = dps::Json::parse("{\"hello\":\"world\",\"n\":1,\"list\":[true,false]}"); + if (value["hello"].to_string_or("") != "world") { + std::cerr << "json parse failed\n"; + return EXIT_FAILURE; + } + if (value["n"].to_int_or(0) != 1) { + std::cerr << "json number failed\n"; + return EXIT_FAILURE; + } + std::cout << "json smoke test passed\n"; + return EXIT_SUCCESS; +} diff --git a/tests/labels_smoke_test.cpp b/tests/labels_smoke_test.cpp new file mode 100644 index 0000000..4f8c17a --- /dev/null +++ b/tests/labels_smoke_test.cpp @@ -0,0 +1,25 @@ +#include "dps/labels.hpp" + +#include +#include + +namespace { + +bool expect_label(int level, const std::string &expected) { + const std::string actual = dps::membership_label(level); + if (actual == expected) { + return true; + } + std::cerr << "unexpected label for level " << level << "\n"; + return false; +} + +} // namespace + +int main() { + if (!expect_label(1, "\xE9\xBB\x84\xE9\x87\x91\xE4\xBC\x9A\xE5\x91\x98")) return EXIT_FAILURE; + if (!expect_label(2, "\xE9\x92\xBB\xE7\x9F\xB3\xE4\xBC\x9A\xE5\x91\x98")) return EXIT_FAILURE; + if (!expect_label(3, "\xE8\x87\xB3\xE8\x87\xBB\xE4\xBC\x9A\xE5\x91\x98")) return EXIT_FAILURE; + if (!expect_label(99, "\xE9\xBB\x84\xE9\x87\x91\xE4\xBC\x9A\xE5\x91\x98")) return EXIT_FAILURE; + return EXIT_SUCCESS; +} diff --git a/tmp-verify.err b/tmp-verify.err new file mode 100644 index 0000000..1f28570 --- /dev/null +++ b/tmp-verify.err @@ -0,0 +1,12 @@ +Listening on 0.0.0.0:65170 +[[2026-04-14 18:39:37] REQ ip=127.0.0.1 method=GET path=/dps/download3/2_%E5%87%8C%E4%BC%97:img.png query=- body_bytes=0 2026-04-14 18:39:37] REQ ip=127.0.0.1 method=GET path=/dps/download3/1_DPS:img.png query=- body_bytes=content_type=- auth=absent +0 [content_type=- auth=absent +2026-04-14 18:39:37] RESP ip=127.0.0.1 method=GET path=/dps/download3/2_%E5%87%8C%E4%BC%97:img.png status=200 content_type=image/png body_bytes=0 binary=no cost_ms=0 +[2026-04-14 18:39:37] RESP ip=127.0.0.1 method=GET path=/dps/download3/1_DPS:img.png status=200 content_type=image/png body_bytes=1146693 binary=yes cost_ms=34 +[2026-04-14 18:39:37] REQ ip=127.0.0.1 method=GET path=/dps/download3/3_%E4%B8%83%E6%B8%B8:img.jpg query=- body_bytes=0 content_type=- auth=absent +[2026-04-14 18:39:37] RESP ip=127.0.0.1 method=GET path=/dps/download3/3_%E4%B8%83%E6%B8%B8:img.jpg status=200 content_type=image/jpeg body_bytes=0 binary=no cost_ms=0 +[2026-04-14 18:39:39] REQ ip=127.0.0.1 method=POST path=/rindro/download/all query=- body_bytes=13 content_type=application/json auth=absent +[2026-04-14 18:39:39] REQ_BODY {"key":"***"} +[2026-04-14 18:39:39] RESP ip=127.0.0.1 method=POST path=/rindro/download/all status=200 content_type=application/octet-stream body_bytes=20093440 binary=yes cost_ms=582 +[2026-04-14 18:39:42] REQ ip=127.0.0.1 method=GET path=/dps/download3/3_%E5%A4%9F%E9%92%9F:img.png query=- body_bytes=0 content_type=- auth=absent +[2026-04-14 18:39:42] RESP ip=127.0.0.1 method=GET path=/dps/download3/3_%E5%A4%9F%E9%92%9F:img.png status=200 content_type=image/png body_bytes=0 binary=no cost_ms=0 diff --git a/tmp-verify.out b/tmp-verify.out new file mode 100644 index 0000000..e69de29 diff --git a/xmake.lua b/xmake.lua new file mode 100644 index 0000000..8418335 --- /dev/null +++ b/xmake.lua @@ -0,0 +1,103 @@ +set_project("dps_manager_server") +set_version("0.1.0") +set_languages("c11", "cxx17") +set_warnings("all", "error") + +add_rules("mode.debug", "mode.release") +add_cxxflags("-Wno-deprecated-declarations", {tools = {"gcc", "clang"}}) +add_requires("openssl3", "zlib", "mariadb-connector-c") + +if is_plat("linux") then + add_syslinks("pthread") +elseif is_plat("mingw", "windows") then + add_defines("DPS_WINDOWS") + add_syslinks("ws2_32") +end + +add_defines("OPENSSL_SUPPRESS_DEPRECATED") + +target("dps_manager_server") + set_kind("binary") + add_includedirs("include") + add_includedirs("third_party/libbcrypt/crypt_blowfish") + add_packages("openssl3", "zlib", "mariadb-connector-c") + add_files("src/*.cpp") + add_files( + "third_party/libbcrypt/crypt_blowfish/wrapper.c", + "third_party/libbcrypt/crypt_blowfish/crypt_blowfish.c", + "third_party/libbcrypt/crypt_blowfish/crypt_gensalt.c", + {warnings = "none"} + ) + add_headerfiles("include/(dps/*.hpp)") + after_build(function (target) + local project_config_dir = path.join(os.projectdir(), "config") + local output_config_dir = path.join(target:targetdir(), "config") + local config_files = { + "server.conf", + "server.conf.example", + "server.windows.conf" + } + + os.mkdir(output_config_dir) + for _, config_file in ipairs(config_files) do + local source = path.join(project_config_dir, config_file) + if os.isfile(source) then + os.cp(source, path.join(output_config_dir, config_file)) + end + end + + if not target:is_plat("windows") then + return + end + local pkg = target:pkg("mariadb-connector-c") + if not pkg then + return + end + local plugin_dir = path.join(pkg:installdir(), "lib", "mariadb", "plugin") + if os.isdir(plugin_dir) then + local output_dir = path.join(target:targetdir(), "mariadb-plugin") + if os.isfile(output_dir) then + os.rm(output_dir) + end + os.mkdir(output_dir) + os.cp(path.join(plugin_dir, "*.dll"), output_dir) + end + end) + +target("json_smoke_test") + set_kind("binary") + set_default(false) + add_includedirs("include") + add_files("tests/json_smoke_test.cpp", "src/json.cpp") + +target("crypto_smoke_test") + set_kind("binary") + set_default(false) + add_includedirs("include") + add_includedirs("third_party/libbcrypt/crypt_blowfish") + add_packages("openssl3", "zlib", "mariadb-connector-c") + add_files( + "tests/crypto_smoke_test.cpp", + "src/app.cpp", + "src/config.cpp", + "src/fs_utils.cpp", + "src/http.cpp", + "src/json.cpp", + "src/labels.cpp", + "third_party/libbcrypt/crypt_blowfish/wrapper.c", + "third_party/libbcrypt/crypt_blowfish/crypt_blowfish.c", + "third_party/libbcrypt/crypt_blowfish/crypt_gensalt.c", + {warnings = "none"} + ) + +target("fs_utils_smoke_test") + set_kind("binary") + set_default(false) + add_includedirs("include") + add_files("tests/fs_utils_smoke_test.cpp", "src/fs_utils.cpp") + +target("labels_smoke_test") + set_kind("binary") + set_default(false) + add_includedirs("include") + add_files("tests/labels_smoke_test.cpp", "src/labels.cpp") diff --git a/旧的java项目/.gitignore b/旧的java项目/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/旧的java项目/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/旧的java项目/.mvn/wrapper/maven-wrapper.jar b/旧的java项目/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..c1dd12f17644411d6e840bd5a10c6ecda0175f18 GIT binary patch literal 58727 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%0ANsA|A3-jj|r`KAo%oTajX3>^E` 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 YM{}PByk10KD_nuQ4E7Du?}+~TKh4V)`~Uy| literal 0 HcmV?d00001 diff --git a/旧的java项目/.mvn/wrapper/maven-wrapper.properties b/旧的java项目/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..d8bc321 --- /dev/null +++ b/旧的java项目/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/旧的java项目/AGENTS.md b/旧的java项目/AGENTS.md new file mode 100644 index 0000000..b134271 --- /dev/null +++ b/旧的java项目/AGENTS.md @@ -0,0 +1,120 @@ +AGENTS.md for this repository + +Purpose +- This document guides agent-like contributors (human or AI) on how to build, lint, test, and style the codebase. +- It also captures preferences or constraints used by the team and by automated agents operating in this repo. + +Scope +- Build, test, and lint commands for common environments (Node/TS, Python, Go, Rust, Java). +- Code style guidelines: imports, formatting, types, naming, error handling, tests, and documentation. +- Cursor rules and Copilot rules inclusion if present in the repo. +- How to handle running a single test and how to extend tests locally. + +1) Quick start: common commands +- Build (preferred entry point): + - Node/TypeScript: npm run build (or yarn build) + - Python: python -m build or your project-specific build script + - Go: go build ./... + - Rust: cargo build +- Lint: + - Node/TypeScript: npm run lint (or yarn lint) + - Python: ruff check . | flake8 or your project’s linter + - Go: golangci-lint run + - Rust: cargo clippy +- Test: + - Node/TS: npm test (or yarn test) + - Python: pytest + - Go: go test ./... + - Rust: cargo test +- Note: If your repo uses a mixed tech stack, prefer using the language-specific script in package.json or equivalent courier scripts. + +2) Run a single test (typical patterns) +- Node / Jest + - Run a specific test by name: npm test -- -t "should render the component" + - Run a specific file: npm test -- path/to/file.test.js +- Python / Pytest + - Run tests matching a keyword: pytest -k "test_name_substring" -q + - Run a specific file: pytest tests/test_module.py -q +- Go + - Run a single test by name: go test -run TestName ./... +- Rust / Cargo + - Exact test: cargo test -- --exact TestName + - Run a file-like subset: cargo test -- 'pattern' +- Java / Maven or Gradle + - Maven: mvn -Dtest=MyTest#testMethod test + - Gradle: ./gradlew test --tests "com.example.MyTest.testMethod" + +3) Code style guidelines + +- Group imports into three blocks: standard library, third-party, and first-party modules. +- Order blocks alphabetically within each group; separate blocks with a newline. +- Avoid wildcard imports; prefer explicit imports. +- For TS/JS, prefer absolute/alias imports over relative when it improves clarity. + + +- Use the project’s formatter (Prettier, gofmt, black, etc.) with the configured settings. +- Respect the repository’s line length (commonly 100-120 chars). Break long lines at logical points. +- Use semicolons consistently if the project enforces them; otherwise adhere to the established style. +- Enable and respect lint rules; fix all autofixable issues during code edits. + + +- In TypeScript, enable strict type checking; prefer interfaces for public APIs and type aliases for shapes. +- Use readonly modifiers where possible to express intent and optimize immutability guarantees. +- Prefer explicit return types for exported functions and public APIs. +- Avoid any where possible; if necessary, use unknown with proper checks. + + +- Variables and functions: camelCase +- Classes and types: PascalCase +- Constants: UPPER_SNAKE_CASE +- File and module names: kebab-case or snake_case, consistent with project convention + + +- Do not swallow errors; attach context when rethrowing (e.g., throw new Error(`Context: ${err.message}`)). +- Propagate errors to callers with meaningful messages. +- Use try/catch around IO-bound or network-bound operations and ensure resources are released in finally or via finally-like blocks. + + +- Prefer async/await syntax for readability. +- Handle rejections at the call site when possible; avoid unhandled promises. +- Use Promise.all when performing independent async tasks, but catch and handle failures gracefully. + + +- Tests should be fast, deterministic, and hermetic. +- Use descriptive test names and structure (Arrange-Act-Assert patterns where helpful). +- Isolate external dependencies; mock/stub network/db calls effectively. +- Include tests for error paths and boundary conditions. + + +- Document non-obvious logic with concise comments; avoid obvious boilerplate. +- Public APIs should have JSDoc / TSdoc-style comments describing inputs, outputs, and side effects. +- Update or add READMEs where necessary to reflect changes in behavior. + + +- Do not log sensitive data; mask secrets in logs. +- Validate inputs and sanitize outputs where appropriate. +- Treat untrusted data carefully; avoid code paths that execute untrusted input without validation. + +4) Cursor and Copilot rules +- Cursor rules: If this repo uses Cursor tooling, its rules can live under .cursor/rules/ or .cursorrules. Copy or adapt them into this document when agents are created. +- Copilot rules: If there is .github/copilot-instructions.md, follow its guidance and ensure code generation adheres to the outlined constraints. +- If the repo contains these files, consider linking to them here and summarizing any special constraints applicable to agents. + +5) Git workflow and contribution notes +- Do not modify dependencies without explicit approval. +- Keep commits small and focused; write 1-2 sentence messages describing why a change was made, not just what changed. +- For AGENTS.md updates, include a short rationale in the commit message. +- Prefer small, reviewed edits over sweeping rewrites. + +6) Local integration guidance +- Run: npm install or yarn to install deps before building/tests. +- For multi-language repos, ensure the local environment has language runtimes and tooling installed (Node, Python, Go, Rust, etc.). +- Use a clean environment (e.g., nvm, virtualenv) to avoid cross-project contamination. + +7) Example usage scenarios +- A single-file feature: create a minimal unit test, run npm test -- -t "feature X" to validate. +- A refactor: run lint and then a subset of tests; fix issues flagged by lints before merging. +- A CI-like dry run: run npm ci; npm run build; npm test; and report failures with minimal noise. + +Notes +- If you want me to tailor this file to the exact repo, I can incorporate the actual existing AGENTS.md content (if present) or sync with Cursor/Copilot rules after inspecting the repo. Right now this is a solid, language-agnostic baseline. diff --git a/旧的java项目/itcast/client/ChatClient.java b/旧的java项目/itcast/client/ChatClient.java new file mode 100644 index 0000000..6809901 --- /dev/null +++ b/旧的java项目/itcast/client/ChatClient.java @@ -0,0 +1,41 @@ +package cn.itcast.client; + +import cn.itcast.protocol.MessageCodecSharable; +import cn.itcast.protocol.ProcotolFrameDecoder; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class ChatClient { + public static void main(String[] args) { + NioEventLoopGroup group = new NioEventLoopGroup(); + LoggingHandler LOGGING_HANDLER = new LoggingHandler(LogLevel.DEBUG); + MessageCodecSharable MESSAGE_CODEC = new MessageCodecSharable(); + try { + Bootstrap bootstrap = new Bootstrap(); + bootstrap.channel(NioSocketChannel.class); + bootstrap.group(group); + bootstrap.handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(new ProcotolFrameDecoder()); + ch.pipeline().addLast(LOGGING_HANDLER); + ch.pipeline().addLast(MESSAGE_CODEC); + } + }); + Channel channel = bootstrap.connect("localhost", 9020).sync().channel(); + channel.closeFuture().sync(); + } catch (Exception e) { +// log.error("client error", e); + } finally { + group.shutdownGracefully(); + } + } +} diff --git a/旧的java项目/itcast/message/AbstractResponseMessage.java b/旧的java项目/itcast/message/AbstractResponseMessage.java new file mode 100644 index 0000000..d52b2e2 --- /dev/null +++ b/旧的java项目/itcast/message/AbstractResponseMessage.java @@ -0,0 +1,19 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public abstract class AbstractResponseMessage extends Message { + private boolean success; + private String reason; + + public AbstractResponseMessage() { + } + + public AbstractResponseMessage(boolean success, String reason) { + this.success = success; + this.reason = reason; + } +} diff --git a/旧的java项目/itcast/message/ChatRequestMessage.java b/旧的java项目/itcast/message/ChatRequestMessage.java new file mode 100644 index 0000000..1bfe03a --- /dev/null +++ b/旧的java项目/itcast/message/ChatRequestMessage.java @@ -0,0 +1,26 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class ChatRequestMessage extends Message { + private String content; + private String to; + private String from; + + public ChatRequestMessage() { + } + + public ChatRequestMessage(String from, String to, String content) { + this.from = from; + this.to = to; + this.content = content; + } + + @Override + public int getMessageType() { + return ChatRequestMessage; + } +} diff --git a/旧的java项目/itcast/message/ChatResponseMessage.java b/旧的java项目/itcast/message/ChatResponseMessage.java new file mode 100644 index 0000000..b87090f --- /dev/null +++ b/旧的java项目/itcast/message/ChatResponseMessage.java @@ -0,0 +1,26 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class ChatResponseMessage extends AbstractResponseMessage { + + private String from; + private String content; + + public ChatResponseMessage(boolean success, String reason) { + super(success, reason); + } + + public ChatResponseMessage(String from, String content) { + this.from = from; + this.content = content; + } + + @Override + public int getMessageType() { + return ChatResponseMessage; + } +} diff --git a/旧的java项目/itcast/message/GroupChatRequestMessage.java b/旧的java项目/itcast/message/GroupChatRequestMessage.java new file mode 100644 index 0000000..68f4d55 --- /dev/null +++ b/旧的java项目/itcast/message/GroupChatRequestMessage.java @@ -0,0 +1,23 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class GroupChatRequestMessage extends Message { + private String content; + private String groupName; + private String from; + + public GroupChatRequestMessage(String from, String groupName, String content) { + this.content = content; + this.groupName = groupName; + this.from = from; + } + + @Override + public int getMessageType() { + return GroupChatRequestMessage; + } +} diff --git a/旧的java项目/itcast/message/GroupChatResponseMessage.java b/旧的java项目/itcast/message/GroupChatResponseMessage.java new file mode 100644 index 0000000..c593d66 --- /dev/null +++ b/旧的java项目/itcast/message/GroupChatResponseMessage.java @@ -0,0 +1,24 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class GroupChatResponseMessage extends AbstractResponseMessage { + private String from; + private String content; + + public GroupChatResponseMessage(boolean success, String reason) { + super(success, reason); + } + + public GroupChatResponseMessage(String from, String content) { + this.from = from; + this.content = content; + } + @Override + public int getMessageType() { + return GroupChatResponseMessage; + } +} diff --git a/旧的java项目/itcast/message/GroupCreateRequestMessage.java b/旧的java项目/itcast/message/GroupCreateRequestMessage.java new file mode 100644 index 0000000..cfc042f --- /dev/null +++ b/旧的java项目/itcast/message/GroupCreateRequestMessage.java @@ -0,0 +1,23 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +import java.util.Set; + +@Data +@ToString(callSuper = true) +public class GroupCreateRequestMessage extends Message { + private String groupName; + private Set members; + + public GroupCreateRequestMessage(String groupName, Set members) { + this.groupName = groupName; + this.members = members; + } + + @Override + public int getMessageType() { + return GroupCreateRequestMessage; + } +} diff --git a/旧的java项目/itcast/message/GroupCreateResponseMessage.java b/旧的java项目/itcast/message/GroupCreateResponseMessage.java new file mode 100644 index 0000000..ac4ad01 --- /dev/null +++ b/旧的java项目/itcast/message/GroupCreateResponseMessage.java @@ -0,0 +1,18 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class GroupCreateResponseMessage extends AbstractResponseMessage { + + public GroupCreateResponseMessage(boolean success, String reason) { + super(success, reason); + } + + @Override + public int getMessageType() { + return GroupCreateResponseMessage; + } +} diff --git a/旧的java项目/itcast/message/GroupJoinRequestMessage.java b/旧的java项目/itcast/message/GroupJoinRequestMessage.java new file mode 100644 index 0000000..9ee3d0f --- /dev/null +++ b/旧的java项目/itcast/message/GroupJoinRequestMessage.java @@ -0,0 +1,22 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class GroupJoinRequestMessage extends Message { + private String groupName; + + private String username; + + public GroupJoinRequestMessage(String username, String groupName) { + this.groupName = groupName; + this.username = username; + } + + @Override + public int getMessageType() { + return GroupJoinRequestMessage; + } +} diff --git a/旧的java项目/itcast/message/GroupJoinResponseMessage.java b/旧的java项目/itcast/message/GroupJoinResponseMessage.java new file mode 100644 index 0000000..6b14427 --- /dev/null +++ b/旧的java项目/itcast/message/GroupJoinResponseMessage.java @@ -0,0 +1,18 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class GroupJoinResponseMessage extends AbstractResponseMessage { + + public GroupJoinResponseMessage(boolean success, String reason) { + super(success, reason); + } + + @Override + public int getMessageType() { + return GroupJoinResponseMessage; + } +} diff --git a/旧的java项目/itcast/message/GroupMembersRequestMessage.java b/旧的java项目/itcast/message/GroupMembersRequestMessage.java new file mode 100644 index 0000000..f2d1361 --- /dev/null +++ b/旧的java项目/itcast/message/GroupMembersRequestMessage.java @@ -0,0 +1,19 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class GroupMembersRequestMessage extends Message { + private String groupName; + + public GroupMembersRequestMessage(String groupName) { + this.groupName = groupName; + } + + @Override + public int getMessageType() { + return GroupMembersRequestMessage; + } +} diff --git a/旧的java项目/itcast/message/GroupMembersResponseMessage.java b/旧的java项目/itcast/message/GroupMembersResponseMessage.java new file mode 100644 index 0000000..d42f737 --- /dev/null +++ b/旧的java项目/itcast/message/GroupMembersResponseMessage.java @@ -0,0 +1,22 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +import java.util.Set; + +@Data +@ToString(callSuper = true) +public class GroupMembersResponseMessage extends Message { + + private Set members; + + public GroupMembersResponseMessage(Set members) { + this.members = members; + } + + @Override + public int getMessageType() { + return GroupMembersResponseMessage; + } +} diff --git a/旧的java项目/itcast/message/GroupQuitRequestMessage.java b/旧的java项目/itcast/message/GroupQuitRequestMessage.java new file mode 100644 index 0000000..847811b --- /dev/null +++ b/旧的java项目/itcast/message/GroupQuitRequestMessage.java @@ -0,0 +1,22 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class GroupQuitRequestMessage extends Message { + private String groupName; + + private String username; + + public GroupQuitRequestMessage(String username, String groupName) { + this.groupName = groupName; + this.username = username; + } + + @Override + public int getMessageType() { + return GroupQuitRequestMessage; + } +} diff --git a/旧的java项目/itcast/message/GroupQuitResponseMessage.java b/旧的java项目/itcast/message/GroupQuitResponseMessage.java new file mode 100644 index 0000000..9ed7107 --- /dev/null +++ b/旧的java项目/itcast/message/GroupQuitResponseMessage.java @@ -0,0 +1,17 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class GroupQuitResponseMessage extends AbstractResponseMessage { + public GroupQuitResponseMessage(boolean success, String reason) { + super(success, reason); + } + + @Override + public int getMessageType() { + return GroupQuitResponseMessage; + } +} diff --git a/旧的java项目/itcast/message/LoginRequestMessage.java b/旧的java项目/itcast/message/LoginRequestMessage.java new file mode 100644 index 0000000..e2a7eab --- /dev/null +++ b/旧的java项目/itcast/message/LoginRequestMessage.java @@ -0,0 +1,26 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class LoginRequestMessage extends Message { + private String username; + private String password; + private String nickname; + + public LoginRequestMessage() { + } + + public LoginRequestMessage(String username, String password, String nickname) { + this.username = username; + this.password = password; + this.nickname = nickname; + } + + @Override + public int getMessageType() { + return LoginRequestMessage; + } +} diff --git a/旧的java项目/itcast/message/LoginResponseMessage.java b/旧的java项目/itcast/message/LoginResponseMessage.java new file mode 100644 index 0000000..12c6858 --- /dev/null +++ b/旧的java项目/itcast/message/LoginResponseMessage.java @@ -0,0 +1,13 @@ +package cn.itcast.message; + +import lombok.Data; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class LoginResponseMessage extends AbstractResponseMessage { + @Override + public int getMessageType() { + return LoginResponseMessage; + } +} diff --git a/旧的java项目/itcast/message/Message.java b/旧的java项目/itcast/message/Message.java new file mode 100644 index 0000000..f0c9f44 --- /dev/null +++ b/旧的java项目/itcast/message/Message.java @@ -0,0 +1,54 @@ +package cn.itcast.message; + +import lombok.Data; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +@Data +public abstract class Message implements Serializable { + + public static Class getMessageClass(int messageType) { + return messageClasses.get(messageType); + } + + private int sequenceId; + + private int messageType; + + public abstract int getMessageType(); + + public static final int LoginRequestMessage = 0; + public static final int LoginResponseMessage = 1; + public static final int ChatRequestMessage = 2; + public static final int ChatResponseMessage = 3; + public static final int GroupCreateRequestMessage = 4; + public static final int GroupCreateResponseMessage = 5; + public static final int GroupJoinRequestMessage = 6; + public static final int GroupJoinResponseMessage = 7; + public static final int GroupQuitRequestMessage = 8; + public static final int GroupQuitResponseMessage = 9; + public static final int GroupChatRequestMessage = 10; + public static final int GroupChatResponseMessage = 11; + public static final int GroupMembersRequestMessage = 12; + public static final int GroupMembersResponseMessage = 13; + private static final Map> messageClasses = new HashMap<>(); + + static { + messageClasses.put(LoginRequestMessage, LoginRequestMessage.class); + messageClasses.put(LoginResponseMessage, LoginResponseMessage.class); + messageClasses.put(ChatRequestMessage, ChatRequestMessage.class); + messageClasses.put(ChatResponseMessage, ChatResponseMessage.class); + messageClasses.put(GroupCreateRequestMessage, GroupCreateRequestMessage.class); + messageClasses.put(GroupCreateResponseMessage, GroupCreateResponseMessage.class); + messageClasses.put(GroupJoinRequestMessage, GroupJoinRequestMessage.class); + messageClasses.put(GroupJoinResponseMessage, GroupJoinResponseMessage.class); + messageClasses.put(GroupQuitRequestMessage, GroupQuitRequestMessage.class); + messageClasses.put(GroupQuitResponseMessage, GroupQuitResponseMessage.class); + messageClasses.put(GroupChatRequestMessage, GroupChatRequestMessage.class); + messageClasses.put(GroupChatResponseMessage, GroupChatResponseMessage.class); + messageClasses.put(GroupMembersRequestMessage, GroupMembersRequestMessage.class); + messageClasses.put(GroupMembersResponseMessage, GroupMembersResponseMessage.class); + } +} diff --git a/旧的java项目/itcast/protocol/MessageCodec.java b/旧的java项目/itcast/protocol/MessageCodec.java new file mode 100644 index 0000000..a772bea --- /dev/null +++ b/旧的java项目/itcast/protocol/MessageCodec.java @@ -0,0 +1,62 @@ +package cn.itcast.protocol; + +import cn.itcast.message.Message; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageCodec; +import lombok.extern.slf4j.Slf4j; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.List; + +@Slf4j +@ChannelHandler.Sharable +public class MessageCodec extends ByteToMessageCodec { + + @Override + public void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception { +// // 1. 4 字节的魔数 +// out.writeBytes(new byte[]{1, 2, 3, 4}); +// // 2. 1 字节的版本, +// out.writeByte(1); +// // 3. 1 字节的序列化方式 jdk 0 , json 1 +// out.writeByte(0); +// // 4. 1 字节的指令类型 +// out.writeByte(msg.getMessageType()); +// // 5. 4 个字节 +//// out.writeInt(msg.getSequenceId()); +// // 无意义,对齐填充 +// out.writeByte(0xff); +// // 6. 获取内容的字节数组 +// ByteArrayOutputStream bos = new ByteArrayOutputStream(); +// ObjectOutputStream oos = new ObjectOutputStream(bos); +// oos.writeObject(msg); +// byte[] bytes = bos.toByteArray(); +// // 7. 长度 +// out.writeInt(bytes.length); +// // 8. 写入内容 +// out.writeBytes(bytes); + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + int magicNum = in.readInt(); + byte version = in.readByte(); + byte serializerType = in.readByte(); + byte messageType = in.readByte(); + int sequenceId = in.readInt(); + in.readByte(); + int length = in.readInt(); + byte[] bytes = new byte[length]; + in.readBytes(bytes, 0, length); + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); + Message message = (Message) ois.readObject(); +// log.debug("{}, {}, {}, {}, {}, {}", magicNum, version, serializerType, messageType, sequenceId, length); +// log.debug("{}", message); + out.add(message); + } +} \ No newline at end of file diff --git a/旧的java项目/itcast/protocol/MessageCodecSharable.java b/旧的java项目/itcast/protocol/MessageCodecSharable.java new file mode 100644 index 0000000..9a9e6f5 --- /dev/null +++ b/旧的java项目/itcast/protocol/MessageCodecSharable.java @@ -0,0 +1,66 @@ +package cn.itcast.protocol; + +import cn.itcast.message.Message; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageCodec; +import lombok.extern.slf4j.Slf4j; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.List; + +@Slf4j +@ChannelHandler.Sharable +/** + * 必须和 LengthFieldBasedFrameDecoder 一起使用,确保接到的 ByteBuf 消息是完整的 + */ +public class MessageCodecSharable extends MessageToMessageCodec { + @Override + protected void encode(ChannelHandlerContext ctx, Message msg, List outList) throws Exception { +// ByteBuf out = ctx.alloc().buffer(); +// // 1. 4 字节的魔数 +// out.writeBytes(new byte[]{1, 2, 3, 4}); +// // 2. 1 字节的版本, +// out.writeByte(1); +// // 3. 1 字节的序列化方式 jdk 0 , json 1 +// out.writeByte(0); +// // 4. 1 字节的指令类型 +// out.writeByte(msg.getMessageType()); +// // 5. 4 个字节 +// out.writeInt(msg.getSequenceId()); +// // 无意义,对齐填充 +// out.writeByte(0xff); +// // 6. 获取内容的字节数组 +// ByteArrayOutputStream bos = new ByteArrayOutputStream(); +// ObjectOutputStream oos = new ObjectOutputStream(bos); +// oos.writeObject(msg); +// byte[] bytes = bos.toByteArray(); +// // 7. 长度 +// out.writeInt(bytes.length); +// // 8. 写入内容 +// out.writeBytes(bytes); +// outList.add(out); + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + int magicNum = in.readInt(); + byte version = in.readByte(); + byte serializerType = in.readByte(); + byte messageType = in.readByte(); + int sequenceId = in.readInt(); + in.readByte(); + int length = in.readInt(); + byte[] bytes = new byte[length]; + in.readBytes(bytes, 0, length); + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); + Message message = (Message) ois.readObject(); +// log.debug("{}, {}, {}, {}, {}, {}", magicNum, version, serializerType, messageType, sequenceId, length); +// log.debug("{}", message); + out.add(message); + } +} diff --git a/旧的java项目/itcast/protocol/ProcotolFrameDecoder.java b/旧的java项目/itcast/protocol/ProcotolFrameDecoder.java new file mode 100644 index 0000000..a1e1fc9 --- /dev/null +++ b/旧的java项目/itcast/protocol/ProcotolFrameDecoder.java @@ -0,0 +1,14 @@ +package cn.itcast.protocol; + +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; + +public class ProcotolFrameDecoder extends LengthFieldBasedFrameDecoder { + + public ProcotolFrameDecoder() { + this(1024, 12, 4, 0, 0); + } + + public ProcotolFrameDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) { + super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip); + } +} diff --git a/旧的java项目/itcast/server/ChatServer.java b/旧的java项目/itcast/server/ChatServer.java new file mode 100644 index 0000000..321b793 --- /dev/null +++ b/旧的java项目/itcast/server/ChatServer.java @@ -0,0 +1,44 @@ +package cn.itcast.server; + +import cn.itcast.protocol.MessageCodecSharable; +import cn.itcast.protocol.ProcotolFrameDecoder; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class ChatServer { + public static void main(String[] args) { + NioEventLoopGroup boss = new NioEventLoopGroup(); + NioEventLoopGroup worker = new NioEventLoopGroup(); + LoggingHandler LOGGING_HANDLER = new LoggingHandler(LogLevel.DEBUG); + MessageCodecSharable MESSAGE_CODEC = new MessageCodecSharable(); + try { + ServerBootstrap serverBootstrap = new ServerBootstrap(); + serverBootstrap.channel(NioServerSocketChannel.class); + serverBootstrap.group(boss, worker); + serverBootstrap.childHandler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(new ProcotolFrameDecoder()); + ch.pipeline().addLast(LOGGING_HANDLER); + ch.pipeline().addLast(MESSAGE_CODEC); + } + }); + Channel channel = serverBootstrap.bind(9020).sync().channel(); + channel.closeFuture().sync(); + } catch (InterruptedException e) { +// log.error("server error", e); + } finally { + boss.shutdownGracefully(); + worker.shutdownGracefully(); + } + } +} diff --git a/旧的java项目/itcast/server/service/UserService.java b/旧的java项目/itcast/server/service/UserService.java new file mode 100644 index 0000000..8a5ec14 --- /dev/null +++ b/旧的java项目/itcast/server/service/UserService.java @@ -0,0 +1,15 @@ +package cn.itcast.server.service; + +/** + * 用户管理接口 + */ +public interface UserService { + + /** + * 登录 + * @param username 用户名 + * @param password 密码 + * @return 登录成功返回 true, 否则返回 false + */ + boolean login(String username, String password); +} diff --git a/旧的java项目/itcast/server/service/UserServiceFactory.java b/旧的java项目/itcast/server/service/UserServiceFactory.java new file mode 100644 index 0000000..c99b3e2 --- /dev/null +++ b/旧的java项目/itcast/server/service/UserServiceFactory.java @@ -0,0 +1,10 @@ +package cn.itcast.server.service; + +public abstract class UserServiceFactory { + + private static UserService userService = new UserServiceMemoryImpl(); + + public static UserService getUserService() { + return userService; + } +} diff --git a/旧的java项目/itcast/server/service/UserServiceMemoryImpl.java b/旧的java项目/itcast/server/service/UserServiceMemoryImpl.java new file mode 100644 index 0000000..e336918 --- /dev/null +++ b/旧的java项目/itcast/server/service/UserServiceMemoryImpl.java @@ -0,0 +1,25 @@ +package cn.itcast.server.service; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class UserServiceMemoryImpl implements UserService { + private Map allUserMap = new ConcurrentHashMap<>(); + + { + allUserMap.put("zhangsan", "123"); + allUserMap.put("lisi", "123"); + allUserMap.put("wangwu", "123"); + allUserMap.put("zhaoliu", "123"); + allUserMap.put("qianqi", "123"); + } + + @Override + public boolean login(String username, String password) { + String pass = allUserMap.get(username); + if (pass == null) { + return false; + } + return pass.equals(password); + } +} diff --git a/旧的java项目/itcast/server/session/Group.java b/旧的java项目/itcast/server/session/Group.java new file mode 100644 index 0000000..53c0dd1 --- /dev/null +++ b/旧的java项目/itcast/server/session/Group.java @@ -0,0 +1,24 @@ +package cn.itcast.server.session; + +import lombok.Data; + +import java.util.Collections; +import java.util.Set; + +@Data +/** + * 聊天组,即聊天室 + */ +public class Group { + // 聊天室名称 + private String name; + // 聊天室成员 + private Set members; + + public static final Group EMPTY_GROUP = new Group("empty", Collections.emptySet()); + + public Group(String name, Set members) { + this.name = name; + this.members = members; + } +} diff --git a/旧的java项目/itcast/server/session/GroupSession.java b/旧的java项目/itcast/server/session/GroupSession.java new file mode 100644 index 0000000..aee06d7 --- /dev/null +++ b/旧的java项目/itcast/server/session/GroupSession.java @@ -0,0 +1,57 @@ +package cn.itcast.server.session; + +import io.netty.channel.Channel; + +import java.util.List; +import java.util.Set; + +/** + * 聊天组会话管理接口 + */ +public interface GroupSession { + + /** + * 创建一个聊天组, 如果不存在才能创建成功, 否则返回 null + * @param name 组名 + * @param members 成员 + * @return 成功时返回组对象, 失败返回 null + */ + Group createGroup(String name, Set members); + + /** + * 加入聊天组 + * @param name 组名 + * @param member 成员名 + * @return 如果组不存在返回 null, 否则返回组对象 + */ + Group joinMember(String name, String member); + + /** + * 移除组成员 + * @param name 组名 + * @param member 成员名 + * @return 如果组不存在返回 null, 否则返回组对象 + */ + Group removeMember(String name, String member); + + /** + * 移除聊天组 + * @param name 组名 + * @return 如果组不存在返回 null, 否则返回组对象 + */ + Group removeGroup(String name); + + /** + * 获取组成员 + * @param name 组名 + * @return 成员集合, 没有成员会返回 empty set + */ + Set getMembers(String name); + + /** + * 获取组成员的 channel 集合, 只有在线的 channel 才会返回 + * @param name 组名 + * @return 成员 channel 集合 + */ + List getMembersChannel(String name); +} diff --git a/旧的java项目/itcast/server/session/GroupSessionFactory.java b/旧的java项目/itcast/server/session/GroupSessionFactory.java new file mode 100644 index 0000000..d300b25 --- /dev/null +++ b/旧的java项目/itcast/server/session/GroupSessionFactory.java @@ -0,0 +1,10 @@ +package cn.itcast.server.session; + +public abstract class GroupSessionFactory { + + private static GroupSession session = new GroupSessionMemoryImpl(); + + public static GroupSession getGroupSession() { + return session; + } +} diff --git a/旧的java项目/itcast/server/session/GroupSessionMemoryImpl.java b/旧的java项目/itcast/server/session/GroupSessionMemoryImpl.java new file mode 100644 index 0000000..2eed343 --- /dev/null +++ b/旧的java项目/itcast/server/session/GroupSessionMemoryImpl.java @@ -0,0 +1,55 @@ +package cn.itcast.server.session; + +import io.netty.channel.Channel; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +public class GroupSessionMemoryImpl implements GroupSession { + private final Map groupMap = new ConcurrentHashMap<>(); + + @Override + public Group createGroup(String name, Set members) { + Group group = new Group(name, members); + return groupMap.putIfAbsent(name, group); + } + + @Override + public Group joinMember(String name, String member) { + return groupMap.computeIfPresent(name, (key, value) -> { +// value.getMembers().add(member); + return value; + }); + } + + @Override + public Group removeMember(String name, String member) { + return groupMap.computeIfPresent(name, (key, value) -> { +// value.getMembers().remove(member); + return value; + }); + } + + @Override + public Group removeGroup(String name) { + return groupMap.remove(name); + } + + @Override + public Set getMembers(String name) { +// return groupMap.getOrDefault(name, Group.EMPTY_GROUP).getMembers(); + return null; + } + + @Override + public List getMembersChannel(String name) { + return getMembers(name).stream() + .map(member -> SessionFactory.getSession().getChannel(member)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } +} diff --git a/旧的java项目/itcast/server/session/Session.java b/旧的java项目/itcast/server/session/Session.java new file mode 100644 index 0000000..667adc7 --- /dev/null +++ b/旧的java项目/itcast/server/session/Session.java @@ -0,0 +1,46 @@ +package cn.itcast.server.session; + + +import io.netty.channel.Channel; + +/** + * 会话管理接口 + */ +public interface Session { + + /** + * 绑定会话 + * @param channel 哪个 channel 要绑定会话 + * @param username 会话绑定用户 + */ + void bind(Channel channel, String username); + + /** + * 解绑会话 + * @param channel 哪个 channel 要解绑会话 + */ + void unbind(Channel channel); + + /** + * 获取属性 + * @param channel 哪个 channel + * @param name 属性名 + * @return 属性值 + */ + Object getAttribute(Channel channel, String name); + + /** + * 设置属性 + * @param channel 哪个 channel + * @param name 属性名 + * @param value 属性值 + */ + void setAttribute(Channel channel, String name, Object value); + + /** + * 根据用户名获取 channel + * @param username 用户名 + * @return channel + */ + Channel getChannel(String username); +} diff --git a/旧的java项目/itcast/server/session/SessionFactory.java b/旧的java项目/itcast/server/session/SessionFactory.java new file mode 100644 index 0000000..1cc5ef4 --- /dev/null +++ b/旧的java项目/itcast/server/session/SessionFactory.java @@ -0,0 +1,10 @@ +package cn.itcast.server.session; + +public abstract class SessionFactory { + + private static Session session = new SessionMemoryImpl(); + + public static Session getSession() { + return session; + } +} diff --git a/旧的java项目/itcast/server/session/SessionMemoryImpl.java b/旧的java项目/itcast/server/session/SessionMemoryImpl.java new file mode 100644 index 0000000..ba63fc6 --- /dev/null +++ b/旧的java项目/itcast/server/session/SessionMemoryImpl.java @@ -0,0 +1,47 @@ +package cn.itcast.server.session; + +import io.netty.channel.Channel; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class SessionMemoryImpl implements Session { + + private final Map usernameChannelMap = new ConcurrentHashMap<>(); + private final Map channelUsernameMap = new ConcurrentHashMap<>(); + private final Map> channelAttributesMap = new ConcurrentHashMap<>(); + + @Override + public void bind(Channel channel, String username) { + usernameChannelMap.put(username, channel); + channelUsernameMap.put(channel, username); + channelAttributesMap.put(channel, new ConcurrentHashMap<>()); + } + + @Override + public void unbind(Channel channel) { + String username = channelUsernameMap.remove(channel); + usernameChannelMap.remove(username); + channelAttributesMap.remove(channel); + } + + @Override + public Object getAttribute(Channel channel, String name) { + return channelAttributesMap.get(channel).get(name); + } + + @Override + public void setAttribute(Channel channel, String name, Object value) { + channelAttributesMap.get(channel).put(name, value); + } + + @Override + public Channel getChannel(String username) { + return usernameChannelMap.get(username); + } + + @Override + public String toString() { + return usernameChannelMap.toString(); + } +} diff --git a/旧的java项目/mvnw b/旧的java项目/mvnw new file mode 100644 index 0000000..8a8fb22 --- /dev/null +++ b/旧的java项目/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/旧的java项目/mvnw.cmd b/旧的java项目/mvnw.cmd new file mode 100644 index 0000000..1d8ab01 --- /dev/null +++ b/旧的java项目/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/旧的java项目/pom.xml b/旧的java项目/pom.xml new file mode 100644 index 0000000..a797043 --- /dev/null +++ b/旧的java项目/pom.xml @@ -0,0 +1,134 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.6 + + + com.example + sjkbf + 0.0.1-SNAPSHOT + sjkbf + Demo project for Spring Boot + + 1.8 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + mysql + mysql-connector-java + 8.0.16 + runtime + + + + net.coobird + thumbnailator + 0.4.14 + + + + com.baomidou + mybatis-plus-boot-starter + 3.4.1 + + + + com.baomidou + mybatis-plus-generator + 3.4.1 + + + + org.freemarker + freemarker + 2.3.31 + + + cn.hutool + hutool-all + 5.7.16 + + + com.baomidou + dynamic-datasource-spring-boot-starter + 3.5.0 + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + + + org.apache.poi + poi-ooxml + 4.1.2 + + + com.google.guava + guava + 31.1-jre + + + + org.springframework.boot + spring-boot-starter-security + + + jakarta.validation + jakarta.validation-api + 2.0.2 + + + + org.jsoup + jsoup + 1.15.3 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/旧的java项目/src/main/java/com/example/sjkbf/ApplicationRunnerImpl.java b/旧的java项目/src/main/java/com/example/sjkbf/ApplicationRunnerImpl.java new file mode 100644 index 0000000..3ea630f --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/ApplicationRunnerImpl.java @@ -0,0 +1,27 @@ +package com.example.sjkbf; + +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.util.Date; + +/** + * @author lnj + * createTime 2018-11-07 22:37 + **/ +@Component +public class ApplicationRunnerImpl implements ApplicationRunner { + @Override + public void run(ApplicationArguments args) throws Exception { + + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/CodeGenerator.java b/旧的java项目/src/main/java/com/example/sjkbf/CodeGenerator.java new file mode 100644 index 0000000..c44cc4e --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/CodeGenerator.java @@ -0,0 +1,141 @@ +package com.example.sjkbf; + +import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; +import com.baomidou.mybatisplus.core.toolkit.StringPool; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.generator.AutoGenerator; +import com.baomidou.mybatisplus.generator.InjectionConfig; +import com.baomidou.mybatisplus.generator.config.*; +import com.baomidou.mybatisplus.generator.config.po.TableInfo; +import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; +import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +public class CodeGenerator { + + + //依赖 + +// compile group: 'com.baomidou', name: 'mybatis-plus-generator', version: '3.3.1.tmp' +// compile group: 'org.freemarker', name: 'freemarker', version: '2.3.31' + + /** + *

+ * 读取控制台内容 + *

+ */ + public static String scanner(String tip) { + Scanner scanner = new Scanner(System.in); + StringBuilder help = new StringBuilder(); + help.append("请输入" + tip + ":"); + System.out.println(help.toString()); + if (scanner.hasNext()) { + String ipt = scanner.next(); + if (StringUtils.isNotBlank(ipt)) { + return ipt; + } + } + throw new MybatisPlusException("请输入正确的" + tip + "!"); + } + + public static void main(String[] args) { + // 代码生成器 + AutoGenerator mpg = new AutoGenerator(); + + // 全局配置 + GlobalConfig gc = new GlobalConfig(); + //获得当前文件路径 + String projectPath = System.getProperty("user.dir"); + gc.setOutputDir(projectPath + "/src/main/java"); +// gc.setOutputDir("D:\\test"); + gc.setAuthor("bozhiqiang"); + ////是否打开资源管理器 + gc.setOpen(false); + // gc.setSwagger2(true); 实体属性 Swagger2 注解 + //service接口:置生成的service接口名首字母是为I + gc.setServiceName("%sService"); + mpg.setGlobalConfig(gc); + + // 数据源配置 + DataSourceConfig dsc = new DataSourceConfig(); + dsc.setUrl("jdbc:mysql://110.42.251.214:3306/rindro?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC&allowPublicKeyRetrieval=true"); + // dsc.setSchemaName("public"); + dsc.setDriverName("com.mysql.cj.jdbc.Driver"); + dsc.setUsername("root"); + dsc.setPassword("Yosin0102!"); + mpg.setDataSource(dsc); + + // 包配置 + PackageConfig pc = new PackageConfig(); + pc.setModuleName(null); + pc.setParent("com.example.zyk"); + //设置实体类包名 可不设 默认entity +// pc.setEntity("entity"); + //设置mapper包名 可不设 默认 mapper +// pc.setMapper("mapper"); + //设置service 包名 可不设默认 service +// pc.setService("service"); + //设置controller 包名 可不设默认 controller +// pc.setController("controller") + mpg.setPackageInfo(pc); + + // 自定义配置 + InjectionConfig cfg = new InjectionConfig() { + @Override + public void initMap() { + // to do nothing + } + }; + + // 如果模板引擎是 freemarker + String templatePath = "/templates/mapper.xml.ftl"; + // 如果模板引擎是 velocity + // String templatePath = "/templates/mapper.xml.vm"; + + // 自定义输出配置 + List focList = new ArrayList<>(); + // 自定义配置会被优先输出 + focList.add(new FileOutConfig(templatePath) { + @Override + public String outputFile(TableInfo tableInfo) { + // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! + return projectPath + "/src/main/resources/mapper/" + + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; + } + }); + + cfg.setFileOutConfigList(focList); + mpg.setCfg(cfg); + + // 配置模板 + TemplateConfig templateConfig = new TemplateConfig(); + + templateConfig.setXml(null); + mpg.setTemplate(templateConfig); + + // 策略配置 + StrategyConfig strategy = new StrategyConfig(); + //表映射实体---去除表前缀 + strategy.setNaming(NamingStrategy.underline_to_camel); + //字段映射实体---去除下划线 + strategy.setColumnNaming(NamingStrategy.underline_to_camel); + //实体类开启Lombok + strategy.setEntityLombokModel(true); + //控制层接口使用Rest + strategy.setRestControllerStyle(true); + //设置要映射的表名 + //String[] tableNames = {"表名1","表名2","","表名3","表名x"}; + //strategy.setInclude(tableNames); + // strategy.setInclude("blog_tags","course","links","sys_settings","user_record"," user_say"); // 设置要映射的表名 + strategy.setInclude(scanner("表名,多个英文逗号分割").split(",")); + strategy.setControllerMappingHyphenStyle(true); + //去除表前缀 + strategy.setTablePrefix("m_"); + mpg.setStrategy(strategy); + mpg.setTemplateEngine(new FreemarkerTemplateEngine()); + mpg.execute(); + } + } \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/SaticScheduleTask.java b/旧的java项目/src/main/java/com/example/sjkbf/SaticScheduleTask.java new file mode 100644 index 0000000..6f46526 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/SaticScheduleTask.java @@ -0,0 +1,110 @@ +package com.example.sjkbf; + +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.LineHandler; +import cn.hutool.core.io.file.Tailer; +import cn.hutool.core.io.watch.SimpleWatcher; +import cn.hutool.core.io.watch.WatchMonitor; +import cn.hutool.core.io.watch.watchers.DelayWatcher; +import cn.hutool.log.StaticLog; +import com.example.sjkbf.common.FileManage; +import com.example.sjkbf.controller.AccountCargoController; +import com.example.sjkbf.service.impl.ProjectPerServiceImpl; +import lombok.AllArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + +import javax.annotation.PostConstruct; +import java.io.*; +import java.nio.file.Path; +import java.nio.file.WatchEvent; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Configuration //1.主要用于标记配置类,兼备Component的效果。 +@EnableScheduling // 2.开启定时任务 +public class SaticScheduleTask { + @Autowired + public ProjectPerServiceImpl projectPerService; + + @Autowired + FileManage fileManage; + + private static ConfigurableApplicationContext context; + //3.添加定时任务 +// @Scheduled(cron = "0 0 0/2 * * ?") + @PostConstruct + public void configureTasks() throws Exception { + fileManage.init(); + + projectPerService.initInfo(); + + WatchMonitor all = WatchMonitor.createAll(fileManage.getScriptfile(), new DelayWatcher(new SimpleWatcher(){ + @Override + public void onModify(WatchEvent event, Path currentPath) { + try { + projectPerService.initInfo(); + + }catch (Exception e){ + e.printStackTrace(); + StaticLog.error("重载项目配置文件出现异常"); + } + System.out.println("重载配置文件"); + } + }, 500)); + + all.setMaxDepth(4); + all.start(); + +// Tailer tailer = new Tailer(FileUtil.file(fileManage.getConfigfile()), new LineHandler() { +// @Override +// public void handle(String s) { +// try { +// projectPerService.initInfo(); +// +// }catch (Exception e){ +// e.printStackTrace(); +// StaticLog.error("重载项目配置文件出现异常"); +// } +// System.out.println("重载配置文件"); +// } +// }, 1); +// +// //新建一个线程 +// Thread thread = new Thread( +// tailer::start +// ); +// thread.start(); + + + + + + } + + @Autowired + AccountCargoController cargoController; + +// @Scheduled(cron = "0 0 1 * * ?") +// private void configureTasks2(){ +// cargoController.save(); +// } + + + public static void main(String[] args) { + System.out.println(2620+1940+1450+741 - ((40+37 +94 +20)*2) -70); + System.out.println(6299*0.7); + System.out.println(4409-25-150-68-200-300-100-50+6+25-200-750-100-400-30-50+70+6-200-50); + } + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/SjkbfApplication.java b/旧的java项目/src/main/java/com/example/sjkbf/SjkbfApplication.java new file mode 100644 index 0000000..d19a8a7 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/SjkbfApplication.java @@ -0,0 +1,59 @@ +package com.example.sjkbf; + +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.context.ConfigurableApplicationContext; + +import java.io.File; +import java.io.IOException; +import java.util.Date; + +@SpringBootApplication() + +public class SjkbfApplication { + + private static ConfigurableApplicationContext context; + + public static void main(String[] args) throws InterruptedException { + SpringApplication.run(SjkbfApplication.class, args); + + + + // 添加自定义关闭钩子,打印触发源 + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + System.out.println("====================================="); + System.out.println("=== 自定义ShutdownHook触发!开始定位原因 ==="); + System.out.println("触发时间:" + new java.util.Date()); + System.out.println("当前线程:" + Thread.currentThread().getName()); + // 打印所有线程的栈轨迹,找到触发关闭的源头 + Thread.getAllStackTraces().forEach((thread, stackTraces) -> { + // 只打印关键线程(减少日志量) + if (thread.getName().contains("ShutdownHook") || thread.getName().contains("main") + || thread.getName().contains("Task") || thread.getName().contains("Timer")) { + System.out.println("\n线程名称:" + thread.getName() + " (状态:" + thread.getState() + ")"); + for (StackTraceElement elem : stackTraces) { + System.out.println("\t" + elem); + } + } + }); + System.out.println("=== ShutdownHook触发原因定位结束 ==="); + System.out.println("====================================="); + + + System.out.println("=== 应用触发关闭,开始重启上下文 ==="); + // 关闭当前上下文 + context.close(); + // 重新启动上下文 + context = SpringApplication.run(SjkbfApplication.class, args); + }, "Custom-Exit-Detector")); + } + + + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/common/FileManage.java b/旧的java项目/src/main/java/com/example/sjkbf/common/FileManage.java new file mode 100644 index 0000000..ae316b7 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/common/FileManage.java @@ -0,0 +1,41 @@ +package com.example.sjkbf.common; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +@Data +public class FileManage { + + @Value("${pro.configfile}") + private String configfile; + + @Value("${pro.userfile}") + private String file; + + + @Value("${pro.filemap}") + private String filemap; + + @Value("${pro.scriptfile}") + private String scriptfile; + + @Value("${pro.dps}") + private String dpsfile; + + private String dpsScriptfile; + + + public void init(){ + + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + dpsScriptfile = file +"dpsScript\\"; + }else { + dpsScriptfile = file +"dpsScript/"; + } + + } + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/config/Configuration/CurrentUserId.java b/旧的java项目/src/main/java/com/example/sjkbf/config/Configuration/CurrentUserId.java new file mode 100644 index 0000000..dd64196 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/config/Configuration/CurrentUserId.java @@ -0,0 +1,11 @@ +package com.example.sjkbf.config.Configuration; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface CurrentUserId { +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/config/Configuration/WebMvcConfig.java b/旧的java项目/src/main/java/com/example/sjkbf/config/Configuration/WebMvcConfig.java new file mode 100644 index 0000000..30b4496 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/config/Configuration/WebMvcConfig.java @@ -0,0 +1,43 @@ +package com.example.sjkbf.config.Configuration; + +import com.example.sjkbf.entity.AuthUser; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.MethodParameter; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(new CurrentUserIdResolver()); + } + + public static class CurrentUserIdResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(CurrentUserId.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String userName = (String)authentication.getPrincipal(); + return userName; + } + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/config/CorsConfig.java b/旧的java项目/src/main/java/com/example/sjkbf/config/CorsConfig.java new file mode 100644 index 0000000..1f8cddd --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/config/CorsConfig.java @@ -0,0 +1,44 @@ +package com.example.sjkbf.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +@Configuration +public class CorsConfig { + + @Bean + public CorsFilter corsFilter() { + // 1. 创建 CORS 配置对象 + CorsConfiguration config = new CorsConfiguration(); + + // 允许的域名(生产环境建议明确指定,而非使用通配符) + config.addAllowedOriginPattern("*"); // Spring Boot 2.4+ 使用 allowedOriginPatterns + // config.addAllowedOrigin("http://localhost:8080"); // 明确指定域名 + + // 允许的请求头 + config.addAllowedHeader("*"); + + // 允许的 HTTP 方法 + config.addAllowedMethod("GET"); + config.addAllowedMethod("POST"); + config.addAllowedMethod("PUT"); + config.addAllowedMethod("DELETE"); + config.addAllowedMethod("OPTIONS"); // 预检请求需要 + + // 是否允许发送 Cookie + config.setAllowCredentials(true); + + // 预检请求缓存时间(秒) + config.setMaxAge(3600L); + + // 2. 注册 CORS 配置应用到所有路径 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + + // 3. 返回 CORS 过滤器 + return new CorsFilter(source); + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/config/JwtAuthenticationTokenFilter.java b/旧的java项目/src/main/java/com/example/sjkbf/config/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000..8c91579 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/config/JwtAuthenticationTokenFilter.java @@ -0,0 +1,87 @@ +package com.example.sjkbf.config; + +import com.example.sjkbf.util.JwtTokenUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.jsonwebtoken.JwtException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { + + @Autowired + private JwtTokenUtil jwtTokenUtil; + + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException { + + + // 1. 从请求头获取 Token + String token = jwtTokenUtil.getTokenFromRequest(request); + try { + // 2. Token 存在且有效(基础验证:签名、过期时间) + if (token != null && jwtTokenUtil.validateToken(token)) { + // 3. 直接从 Token 中解析用户信息和权限(无需查库) + String username = jwtTokenUtil.getUsernameFromToken(token); + List authorities = jwtTokenUtil.getAuthoritiesFromToken(token) + .stream() + .map(SimpleGrantedAuthority ::new) + .collect(Collectors.toList()); + + // 4. 构建 Authentication 对象(权限来自 Token) + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken( + username, // 主体可以是用户名(或自定义 User 对象) + null, // 凭证(密码)无需存储 + authorities + ); + + // 5. 存入 SecurityContext(后续鉴权直接使用 Token 中的权限) + SecurityContextHolder.getContext().setAuthentication(authentication); + } + }catch (JwtException e){ + sendErrorResponse(response, 4011, "请重新登录"); + return; + } + catch (Exception e) { + // Token 解析失败(如篡改、过期),清理上下文 + SecurityContextHolder.clearContext(); + sendErrorResponse(response, 4013, "认证失败"); + logger.error("认证失败:" + e.getMessage()); + return; + } + + // 6. 继续过滤器链 + chain.doFilter(request, response); + } + + + // 发送 JSON 错误响应 + private void sendErrorResponse(HttpServletResponse response, int code, String message) + throws IOException { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setContentType("application/json;charset=UTF-8"); + + Map errorData = new HashMap<>(); + errorData.put("code", code); + errorData.put("msg", message); + + response.getWriter().write(new ObjectMapper().writeValueAsString(errorData)); + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/config/MyMetaObjectHandler.java b/旧的java项目/src/main/java/com/example/sjkbf/config/MyMetaObjectHandler.java new file mode 100644 index 0000000..5ab44d2 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/config/MyMetaObjectHandler.java @@ -0,0 +1,26 @@ +package com.example.sjkbf.config; + +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; +import java.time.LocalDateTime; +import java.util.Date; + +@Component +public class MyMetaObjectHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + // 插入时自动填充创建时间 + this.strictInsertFill(metaObject, "createTime", Date.class, DateUtil.date()); + // 插入时自动填充更新时间 + this.strictInsertFill(metaObject, "updateTime", Date.class, DateUtil.date()); + } + + @Override + public void updateFill(MetaObject metaObject) { + // 更新时自动填充更新时间 + this.strictUpdateFill(metaObject, "updateTime", Date.class, DateUtil.date()); + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/config/RateLimitConfig.java b/旧的java项目/src/main/java/com/example/sjkbf/config/RateLimitConfig.java new file mode 100644 index 0000000..df9d12d --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/config/RateLimitConfig.java @@ -0,0 +1,16 @@ +package com.example.sjkbf.config; + +import com.google.common.util.concurrent.RateLimiter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RateLimitConfig { + // 总带宽限制:15MB/s = 15 * 1024 * 1024 B/s + private static final double TOTAL_BANDWIDTH = 15 * 1024 * 1024; + + @Bean + public RateLimiter globalRateLimiter() { + return RateLimiter.create(TOTAL_BANDWIDTH); + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/config/SecurityConfig.java b/旧的java项目/src/main/java/com/example/sjkbf/config/SecurityConfig.java new file mode 100644 index 0000000..fe2df1a --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/config/SecurityConfig.java @@ -0,0 +1,134 @@ +package com.example.sjkbf.config; + +import com.example.sjkbf.config.exception.CustomAuthenticationEntryPoint; +import com.example.sjkbf.service.impl.UserDetailsServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.access.AccessDeniedHandlerImpl; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.filter.CorsFilter; + +/** + * Spring Security 核心配置类 + * - 继承 WebSecurityConfigurerAdapter(适配 Spring Boot 2.5.x) + * - 启用方法级权限控制(@PreAuthorize) + * - 配置 JWT 过滤器、密码加密、认证规则等 + */ +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法级权限注解 +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private UserDetailsServiceImpl userDetailsService; // 用户服务(需自行实现) + + @Autowired + private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; // JWT 过滤器 + + @Autowired + private CorsFilter corsFilter; // 跨域过滤器(需自行配置) + + // 认证失败处理器 + @Autowired + private CustomAuthenticationEntryPoint unauthorizedHandler; + + +// @Autowired +// private LogoutSuccessHandlerImpl logoutSuccessHandler; // 注销成功处理器 + + /** + * 密码加密器(强制使用 BCrypt) + */ + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + /** + * 认证管理器(暴露为 Bean,供登录接口调用) + */ + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + /** + * 配置用户认证来源(如数据库) + */ + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + //注册验证用户的处理器 和密码加密器 + auth.userDetailsService(userDetailsService) + .passwordEncoder(passwordEncoder()); + } + + /** + * 核心安全配置 + */ + @Override + protected void configure(HttpSecurity http) throws Exception { + http + // ==================== 基础配置 ==================== + .csrf().disable() // 关闭 CSRF(REST API 无状态,无需 CSRF) + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态会话 + .and() + .exceptionHandling() + .authenticationEntryPoint(unauthorizedHandler) // 认证失败处理(如未登录访问受保护接口) + .accessDeniedHandler(new AccessDeniedHandlerImpl()) // 权限不足处理(已登录但无权限) + .and() + + // ==================== 授权规则 ==================== + .authorizeRequests() + // 开放接口(无需认证) + .antMatchers( + "/login", + "/register", + "/changePassword", + "/dps/*", + "/dps/*/*", + "/rindro/download", + "/rindro/download/*", + "/rindro/mp4", + "/rindro/getversion", + "/rindro/addgold", + "/rindro/getimg/*/*", + "/rindro/getthumb/*/*", + + "/rindro/getmp4/*/*", + "/rindro/getmp42/*/*", + "/script/*", + "/api/videos/*/*", + "/zf/callback", + "/rindro/download/*/*" + ).permitAll() + // 其他所有请求需认证 + .anyRequest().authenticated() + .and() + + // ==================== 过滤器配置 ==================== + + // 添加 CORS 过滤器在最前 + .addFilterBefore(corsFilter, LogoutFilter.class) + // 添加 JWT 过滤器在登录认证过滤器之前 + .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + + + // ==================== 注销配置 ==================== +// .logout() +// .logoutUrl("/logout") // 注销接口 +// .logoutSuccessHandler(logoutSuccessHandler) // 注销成功处理(返回 JSON) +// .deleteCookies("JSESSIONID"); // 删除 Cookie(如果使用) + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/config/StartInit.java b/旧的java项目/src/main/java/com/example/sjkbf/config/StartInit.java new file mode 100644 index 0000000..0b4dba2 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/config/StartInit.java @@ -0,0 +1,25 @@ +package com.example.sjkbf.config; + +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.example.sjkbf.controller.AccountCargoController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.Date; + +@Configuration //1.主要用于标记配置类,兼备Component的效果。 +@EnableScheduling // 2.开启定时任务 +public class StartInit { + + + @Scheduled(cron = "0 0 0 * * ?") + public void configureTasks() throws Exception { + AccountCargoController.a.clear(); + } + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/config/VideoConfig.java b/旧的java项目/src/main/java/com/example/sjkbf/config/VideoConfig.java new file mode 100644 index 0000000..e78345b --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/config/VideoConfig.java @@ -0,0 +1,31 @@ +package com.example.sjkbf.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; +import org.springframework.web.servlet.config.annotation.*; +import org.springframework.web.servlet.resource.EncodedResourceResolver; + +import java.util.concurrent.TimeUnit; + +@Configuration +public class VideoConfig implements WebMvcConfigurer { + + /** + * 配置视频文件缓存策略 + */ + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + // 1. 转码视频资源映射(/tmp/video-transcoded/) + registry.addResourceHandler("/transcoded/**") + .addResourceLocations("file:/tmp/video-transcoded/") + .setCacheControl(CacheControl.maxAge(30, TimeUnit.DAYS)) + .resourceChain(true) + .addResolver(new EncodedResourceResolver()); + + // 2. 原始视频资源映射(/root/rindro/Script/Project/) + registry.addResourceHandler("/original/**") + .addResourceLocations("file:/root/rindro/Script/Project/") + .setCacheControl(CacheControl.noCache()) + .resourceChain(false); + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/config/exception/CustomAuthenticationEntryPoint.java b/旧的java项目/src/main/java/com/example/sjkbf/config/exception/CustomAuthenticationEntryPoint.java new file mode 100644 index 0000000..10d01eb --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/config/exception/CustomAuthenticationEntryPoint.java @@ -0,0 +1,36 @@ +package com.example.sjkbf.config.exception; + +import cn.hutool.log.StaticLog; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +@Component +public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { + + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException { + // 默认处理,实际应由全局异常处理器接管 + // 设置响应状态码和内容类型 + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setContentType("application/json;charset=UTF-8"); + + // 构建自定义错误信息 + Map errorData = new HashMap<>(); + errorData.put("code", 4010); + errorData.put("msg", "此页面需要登录后访问"); + + // 写入 JSON 响应 + response.getWriter().write(new ObjectMapper().writeValueAsString(errorData)); + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/config/exception/GlobalExceptionHandler.java b/旧的java项目/src/main/java/com/example/sjkbf/config/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..b5d0add --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/config/exception/GlobalExceptionHandler.java @@ -0,0 +1,70 @@ +package com.example.sjkbf.config.exception; + +import cn.hutool.log.StaticLog; +import com.example.sjkbf.entity.CommonResult; + +import org.apache.catalina.connector.ClientAbortException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + + + // 处理用户名密码错误异常(Spring Security 默认抛出 BadCredentialsException) + @ExceptionHandler(BadCredentialsException.class) + public ResponseEntity handleBadCredentials(BadCredentialsException e) { + + Map response = new HashMap<>(); + response.put("msg", "用户名或密码错误"); + response.put("code", 4010); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .body(response); + } + + + @ExceptionHandler(RuntimeException.class) + public ResponseEntity> responseEntity(RuntimeException e) { + + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.CREATED) + .body(CommonResult.failed(201,e.getMessage())); + } + + + /** + * 处理视频文件不存在异常 + */ + @ExceptionHandler(FileNotFoundException.class) + public ResponseEntity handleFileNotFound(FileNotFoundException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body("Video file not found: " + e.getMessage()); + } + + /** + * 处理转码异常 + */ + @ExceptionHandler(IOException.class) + public ResponseEntity handleTranscodeError(IOException e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body("Video processing error: " + e.getMessage()); + } + + + @ExceptionHandler(ClientAbortException.class) + public void handleClientAbortException(ClientAbortException e) { + // 仅打印警告日志,无需向上抛出 + StaticLog.info("客户端主动断开连接: {}", e.getMessage()); + } + +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/controller/AccountCargoController.java b/旧的java项目/src/main/java/com/example/sjkbf/controller/AccountCargoController.java new file mode 100644 index 0000000..6e0f575 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/controller/AccountCargoController.java @@ -0,0 +1,328 @@ +package com.example.sjkbf.controller; + + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.file.FileWriter; +import cn.hutool.log.StaticLog; +import com.example.sjkbf.common.FileManage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import cn.hutool.core.io.file.FileReader; + +import org.springframework.http.ResponseEntity; + + +import org.springframework.web.bind.annotation.*; + + +import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

+ * InnoDB free: 11264 kB 前端控制器 + *

+ * + * @author bozhiqiang + * @since 2022-04-03 + */ +@RestController +@RequestMapping("/dps") +public class AccountCargoController { + + @Autowired + FileManage fileManage; + + public static Map a=new HashMap<>(); + + @RequestMapping("/login") + public int login(HttpServletRequest request) { + + String ipAddress = getIpAddress(request); + a.put(ipAddress,a.size()+1); + return a.size(); + } + + @RequestMapping("/getversion") + public String getversion() { + + FileReader fileReader = new FileReader(fileManage.getDpsfile()+"Application/Proj.ifo"); + String result = fileReader.readString(); + return result; + } + + + @RequestMapping("/getclientversion") + public String getclientversion() { + + FileReader fileReader = new FileReader(fileManage.getDpsfile()+"Marketplace/Proj.ifo"); + String result = fileReader.readString(); + + return result; + } + + @RequestMapping("/getlist") + public String getlist() { + + + File[] ls = FileUtil.ls(fileManage.getDpsfile()+"Script"); + + StringBuilder list = new StringBuilder("{ \"list\":["); + for (File l : ls) { + FileReader fileReader = new FileReader(l+"/"+"Proj.ifo"); + + list.append(fileReader.readString()).append(","); + } + list.deleteCharAt(list.length() - 1); + list.append("]}"); + return list.toString(); + } + + @RequestMapping("/getadvertisement") + public String getadvertisement() { + + + File[] ls = FileUtil.ls(fileManage.getDpsfile()+"Advertisement"); + //按文件夹名字排序 + Arrays.sort(ls, Comparator.comparing(File::getName)); + StringBuilder list = new StringBuilder("{ \"list\":["); + for (File l : ls) { + FileReader fileReader = new FileReader(l+"/"+"info.ifo"); + + list.append(fileReader.readString()).append(","); + } + list.deleteCharAt(list.length() - 1); + list.append("]}"); + return list.toString(); + } + + // 使用正则表达式捕获所有字符(包括斜杠) + @PostMapping("/download/{colonPath}") + public ResponseEntity download( + @PathVariable String colonPath) throws Exception { + + // 1. 解码中文路径 + String decodedPath = URLDecoder.decode(colonPath, "UTF-8"); +// System.out.println("接收到参数: " + decodedPath); + + // 2. 将冒号还原为斜杠 + String realPath = decodedPath.replace(":", "/"); +// System.out.println("真实路径: " + realPath); + StaticLog.info( "文件下载1: " + realPath ); + // 3. 构建绝对路径 + Path baseDir = Paths.get(fileManage.getDpsfile()+"Script").toAbsolutePath().normalize(); + Path targetPath = baseDir.resolve(realPath).normalize(); + + // 4. 安全校验(防止路径穿越) + if (!targetPath.startsWith(baseDir)) { + System.err.println("非法路径尝试: " + realPath); + return ResponseEntity.status(403).build(); + } + + // 5. 返回文件 + Resource resource = new FileSystemResource(targetPath); + if (!resource.exists()) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok() + .header("Content-Disposition", + "attachment; filename=\"" + resource.getFilename() + "\"") + .body(resource); + } + + + // 使用正则表达式捕获所有字符(包括斜杠) + @PostMapping ("/download2/{colonPath}") + public ResponseEntity download2( + @PathVariable String colonPath) throws Exception { + + // 1. 解码中文路径 + String decodedPath = URLDecoder.decode(colonPath, "UTF-8"); +// System.out.println("接收到参数: " + decodedPath); + + // 2. 将冒号还原为斜杠 + String realPath = decodedPath.replace(":", "/"); +// System.out.println("真实路径: " + realPath); + // 3. 构建绝对路径 + Path baseDir = Paths.get(fileManage.getDpsfile()+"Application").toAbsolutePath().normalize(); + Path targetPath = baseDir.resolve(realPath).normalize(); + + // 4. 安全校验(防止路径穿越) + if (!targetPath.startsWith(baseDir)) { + System.err.println("非法路径尝试: " + realPath); + return ResponseEntity.status(403).build(); + } + + // 5. 返回文件 + Resource resource = new FileSystemResource(targetPath); + if (!resource.exists()) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok() + .header("Content-Disposition", + "attachment; filename=\"" + resource.getFilename() + "\"") + .body(resource); + } + + @RequestMapping("/download3/{colonPath}") + public ResponseEntity download3( + @PathVariable String colonPath) throws Exception { + + // 1. 解码中文路径 + String decodedPath = URLDecoder.decode(colonPath, "UTF-8"); +// System.out.println("接收到参数: " + decodedPath); + + // 2. 将冒号还原为斜杠 + String realPath = decodedPath.replace(":", "/"); +// System.out.println("真实路径: " + realPath); + + // 3. 构建绝对路径 + Path baseDir = Paths.get(fileManage.getDpsfile()+"Advertisement").toAbsolutePath().normalize(); + Path targetPath = baseDir.resolve(realPath).normalize(); + + // 4. 安全校验(防止路径穿越) + if (!targetPath.startsWith(baseDir)) { + System.err.println("非法路径尝试: " + realPath); + return ResponseEntity.status(403).build(); + } + + // 5. 返回文件 + Resource resource = new FileSystemResource(targetPath); + if (!resource.exists()) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok() + .header("Content-Disposition", + "attachment; filename=\"" + resource.getFilename() + "\"") + .body(resource); + } + + public static void main(String[] args) { + + File[] ls = FileUtil.ls("C:\\Users\\12134\\Downloads\\Advertisement"); + + StringBuilder list = new StringBuilder("{ \"list\":["); + for (File l : ls) { + FileReader fileReader = new FileReader(l+"\\"+"info.ifo"); + + list.append(fileReader.readString()).append(","); + } + list.deleteCharAt(list.length() - 1); + list.append("]}"); + + System.out.println(list.toString()); + } + + //添加 + public static String processCommand(String input) { + // 按行处理以保留格式 + String[] lines = input.split("\n"); + StringBuilder result = new StringBuilder(); + + for (String line : lines) { + // 处理包含df_game_r的命令行(包括注释行) + if (line.contains("./df_game_r siroco")) { + line = processGameLine(line); + } + + result.append(line).append("\n"); + } + + return result.toString().trim(); + } + + private static String processGameLine(String line) { + // 检查是否是注释行 + boolean isCommented = line.trim().startsWith("#"); + String workingLine = isCommented ? line.substring(line.indexOf("#") + 1) : line; + + // 检查是否已有LD_PRELOAD + Matcher preloadMatcher = Pattern.compile("LD_PRELOAD\\s*=\\s*\"([^\"]*)\"").matcher(workingLine); + + if (preloadMatcher.find()) { + // 已有LD_PRELOAD的情况:确保包含目标库 + String libs = preloadMatcher.group(1).trim(); + if (!libs.contains("/dp_s/lib/libAurora.so")) { + String newLibs = "/dp_s/lib/libAurora.so" + (libs.isEmpty() ? "" : " " + libs); + workingLine = workingLine.replaceFirst("LD_PRELOAD\\s*=\\s*\"[^\"]*\"", + "LD_PRELOAD=\" " + newLibs + "\""); + } + } else { + // 没有LD_PRELOAD的情况:添加 + workingLine = workingLine.replaceFirst("(\\./df_game_r\\s+siroco)", + "LD_PRELOAD=\" /dp_s/lib/libAurora.so\" $1"); + } + + return isCommented ? "#" + workingLine : workingLine; + } + + //删除 + public static String cleanLdPreload(String input) { + // 使用Matcher的region功能保留所有换行符 + StringBuilder result = new StringBuilder(); + Pattern pattern = Pattern.compile( + "(LD_PRELOAD\\s*=\\s*\")([^\"]*?)/dp_s/lib/libAurora\\.so([^\"]*?)(\")|" + + "(LD_PRELOAD\\s*=\\s*\"\\s*\")" + ); + + Matcher matcher = pattern.matcher(input); + int lastPos = 0; + + while (matcher.find()) { + // 添加匹配前的内容(包含所有换行符) + result.append(input, lastPos, matcher.start()); + + // 处理匹配到的内容 + if (matcher.group(1) != null) { // 情况1:包含Aurora.so + String replacement = matcher.group(1) + + matcher.group(2) + + matcher.group(3) + + matcher.group(4); + // 如果替换后变成LD_PRELOAD="",则完全删除 + if (replacement.equals("LD_PRELOAD=\"\"")) { + // 不添加任何内容(相当于删除) + } else { + result.append(replacement); + } + } else { // 情况2:空的LD_PRELOAD + // 完全跳过不添加(删除) + } + + lastPos = matcher.end(); + } + + // 添加剩余内容 + result.append(input.substring(lastPos)); + + return result.toString(); + } + + public static String getIpAddress(HttpServletRequest request) { + // 获取请求的IP地址 + String ipAddress = request.getRemoteAddr(); + // 如果应用部署在反向代理或负载均衡器后面,可以通过X-Forwarded-For头部获取真实IP地址 + String forwardedFor = request.getHeader("X-Forwarded-For"); + if (forwardedFor != null && !forwardedFor.isEmpty()) { + ipAddress = forwardedFor.split(",")[0]; + } + return ipAddress; + } + + + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/controller/AuthController.java b/旧的java项目/src/main/java/com/example/sjkbf/controller/AuthController.java new file mode 100644 index 0000000..03b752b --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/controller/AuthController.java @@ -0,0 +1,103 @@ +package com.example.sjkbf.controller; + +import com.example.sjkbf.entity.CommonResult; +import com.example.sjkbf.entity.DTO.LoginRequest; +import com.example.sjkbf.entity.DTO.LoginResponse; +import com.example.sjkbf.service.UserService; +import com.example.sjkbf.util.JwtTokenUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; + +import javax.validation.Valid; +import java.util.List; +import java.util.stream.Collectors; + +@RestController +public class AuthController { + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private JwtTokenUtil jwtTokenUtil; + @Autowired + private UserService userService; + @PostMapping("/login") + public ResponseEntity login(@Valid @RequestBody LoginRequest request) { + // 1. 创建认证令牌(未认证状态) + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()); + + // 2. 执行认证(Spring Security 自动调用 UserDetailsService) + Authentication authentication = authenticationManager.authenticate(authenticationToken); + + // 3. 认证成功,生成 JWT + UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + String token = jwtTokenUtil.generateToken(userDetails); + + // 4. 提取权限信息 + List roles = userDetails.getAuthorities().stream() + + .map(GrantedAuthority::getAuthority) + .collect(Collectors.toList()); + + // 5. 返回响应 + return ResponseEntity.ok( + new LoginResponse("Bearer "+token, userDetails.getUsername(), roles) + ); + } + + // 用户注册接口 + @PostMapping("/register") + public ResponseEntity> register(@Valid @RequestBody LoginRequest request) { + if (request.getUsername()==null|| request.getUsername().isEmpty()) { + return ResponseEntity.status(HttpStatus.CREATED).body(CommonResult.failed(201,"用户名为空")); + } + if (request.getPassword()==null|| request.getPassword().isEmpty()) { + return ResponseEntity.status(HttpStatus.CREATED).body(CommonResult.failed(201,"密码为空")); + } + if (request.getQq()==null|| request.getQq().isEmpty()) { + return ResponseEntity.status(HttpStatus.CREATED).body(CommonResult.failed(201,"安全口令为空")); + } + + // 检查用户名是否已存在 + if (userService.isUsernameExist(request.getUsername())) { + return ResponseEntity.status(HttpStatus.CREATED).body(CommonResult.failed(201,"用户名已存在")); + } + + + // 保存用户 + userService.registerUser(request); + return ResponseEntity.ok(CommonResult.success("注册成功")); + } + + //找回密码 + @PostMapping("/changePassword") + public ResponseEntity> changePassword(@Valid @RequestBody LoginRequest request) { + if (request.getUsername()==null|| request.getUsername().isEmpty()) { + return ResponseEntity.status(HttpStatus.CREATED).body(CommonResult.failed(201,"用户名为空")); + } + if (request.getPassword()==null|| request.getPassword().isEmpty()) { + return ResponseEntity.status(HttpStatus.CREATED).body(CommonResult.failed(201,"密码为空")); + } + if (request.getQq()==null|| request.getQq().isEmpty()) { + return ResponseEntity.status(HttpStatus.CREATED).body(CommonResult.failed(201,"安全口令为空")); + } + + // 保存用户 + userService.updatePassword(request); + return ResponseEntity.ok(CommonResult.success("修改成功")); + } + +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/controller/UserController.java b/旧的java项目/src/main/java/com/example/sjkbf/controller/UserController.java new file mode 100644 index 0000000..a69b441 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/controller/UserController.java @@ -0,0 +1,699 @@ +package com.example.sjkbf.controller; + + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.file.FileReader; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.hutool.log.StaticLog; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.example.sjkbf.common.FileManage; +import com.example.sjkbf.config.Configuration.CurrentUserId; +import com.example.sjkbf.entity.*; +import com.example.sjkbf.entity.projectconfig.Project; +import com.example.sjkbf.service.ProjectPerService; +import com.example.sjkbf.service.UserAndServerService; +import com.example.sjkbf.service.UserServerProjectService; +import com.example.sjkbf.service.UserService; +import com.google.common.util.concurrent.RateLimiter; +import net.coobird.thumbnailator.Thumbnails; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.*; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLDecoder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 前端控制器 + *

+ * + * @author bozhiqiang + * @since 2025-04-15 + */ +@RestController +@RequestMapping("/rindro") +public class UserController { + + @Autowired + private ProjectPerService projectPerService; + + @Autowired + private UserService userService; + + + @Autowired + private UserAndServerService userAndServerService; + + @Autowired + private UserServerProjectService userServerProjectService; + + @Autowired + FileManage fileManage; + + + //下载插件相关内容 + @PostMapping("/download") + public ResponseEntity download(@RequestBody Map body) throws Exception { +// //如果不是通用文件就要先检查 +// if (!body.get("projectName").contains("通用")) { +// List byUserName = userServerProjectService.getByUserName(userName); +// if (byUserName == null) { +// // 若服务器没有该插件,返回错误信息的通用结果 +// return new ResponseEntity<>(CommonResult.failed("没有插件授权 无法下载相关内容"), HttpStatus.OK); +// } +// for (UserServerProject userServerProject : byUserName) { +// boolean isExpired = false; +// if (userServerProject.getProjectName().equals(body.get("projectName"))) { +// isExpired =true; +// } +// if (!isExpired){ +// return new ResponseEntity<>(CommonResult.failed("没有插件授权 无法下载相关内容"), HttpStatus.OK); +// } +// } +// } + String file = "/root/rindro/download/profile/"+body.get("projectName")+".zip"; + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + file = "C:\\Users\\12134\\Desktop\\工作目录\\rindro\\download\\profile\\"+body.get("projectName")+".zip"; + } + + Path baseDir = Paths.get(file).toAbsolutePath().normalize(); + Resource resource = new FileSystemResource(baseDir); + + // 设置响应头 + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\""); + + // 返回包含文件资源的通用结果 + return ResponseEntity.ok() + .header("Content-Disposition", + "attachment; filename=\"" + resource.getFilename() + "\"") + .body(resource); + } + + + + //获得账号信息 + @PostMapping("/getInfo") + public ResponseEntity>> getInfo(@CurrentUserId String userName) { + User user = userService.getOne(new QueryWrapper().eq("username", userName)); + + Map body = new HashMap<>(); + body.put("userpoints", user.getGold()); + body.put("username", user.getName()); + body.put("useravatar", user.getImg()); + body.put("uservip", "普通会员"); + body.put("usercash", null); + + return ResponseEntity.ok(CommonResult.success(body)); + } + +// +// /** +// * 获得项目的标题文件 +// * @param colonPath +// * @return +// * @throws UnsupportedEncodingException +// */ +// @RequestMapping("/getimg/{colonPath}") +// public ResponseEntity getimg(@PathVariable String colonPath) throws UnsupportedEncodingException { +// String decodedPath = URLDecoder.decode(colonPath, "UTF-8"); +// +// Project byName = projectPerService.getByName(decodedPath); +// if (byName==null)return null; +// String imgPath = byName.getImgPath(); +// +// String file = "/root/rindro/Script/"+imgPath; +// String os = System.getProperty("os.name").toLowerCase(); +// if (os.contains("win")) { +// file = "C:\\Users\\12134\\Desktop\\工作目录\\rindro\\Script\\"+imgPath; +// } +// +// +// // 5. 返回文件 +// Resource resource = new FileSystemResource(file); +// if (!resource.exists()) { +// return ResponseEntity.notFound().build(); +// } +// +// return ResponseEntity.ok() +// .header("Content-Disposition", +// "attachment; filename=\"" + resource.getFilename() + "\"") +// .body(resource); +// } + + /** + * 获得项目文件中的图片 + * @param colonPath + * @return + * @throws UnsupportedEncodingException + */ + @RequestMapping("/getimg/{colonPath}/{img}") + public ResponseEntity getimg2(@PathVariable String colonPath,@PathVariable String img) throws UnsupportedEncodingException { + String decodedPath = URLDecoder.decode(colonPath, "UTF-8"); + img = URLDecoder.decode(img, "UTF-8"); + + Project byName = projectPerService.getByName(decodedPath); + + if (byName==null)return null; + String imgPath = byName.getImgPath(); + + String file = "/root/rindro/Script/Project/"+imgPath+"/img/"+img+".png"; + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + file = "C:\\Users\\12134\\Desktop\\工作目录\\rindro\\Script\\Project\\"+imgPath+"\\img\\"+img+".png"; + } + // 5. 返回文件 + Resource resource = new FileSystemResource(file); + if (!resource.exists()) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok() + .header("Content-Disposition", + "attachment; filename=\"" + resource.getFilename() + "\"") + .body(resource); + } + + + /** + * 获得项目文件中的缩略图(使用Thumbnailator) + */ + @RequestMapping("/getthumb/{colonPath}/{img}") + public ResponseEntity getThumbnailWithLib( + @PathVariable String colonPath, + @PathVariable String img, + @RequestParam(required = false, defaultValue = "200") int width, + @RequestParam(required = false, defaultValue = "200") int height) + throws UnsupportedEncodingException, IOException { + + String decodedPath = URLDecoder.decode(colonPath, "UTF-8"); + img = URLDecoder.decode(img, "UTF-8"); + + Project byName = projectPerService.getByName(decodedPath); + if (byName == null) return ResponseEntity.notFound().build(); + + String imgPath = byName.getImgPath(); + String originalFile = "/root/rindro/Script/Project/" + imgPath + "/img/" + img + ".png"; + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + originalFile = "C:\\Users\\12134\\Desktop\\工作目录\\rindro\\Script\\Project\\" + imgPath + "\\img\\" + img + ".png"; + } + + // 检查原图是否存在 + Resource originalResource = new FileSystemResource(originalFile); + if (!originalResource.exists()) { + return ResponseEntity.notFound().build(); + } + + // 使用Thumbnailator生成缩略图 + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + Thumbnails.of(originalFile) + .size(width, height) + .outputFormat("png") + .outputQuality(0.8) // 设置输出质量 + .toOutputStream(outputStream); + + byte[] thumbnailBytes = outputStream.toByteArray(); + ByteArrayResource resource = new ByteArrayResource(thumbnailBytes); + + String downloadFileName = img + "_thumb_" + width + "x" + height + ".png"; + + return ResponseEntity.ok() + .header("Content-Type", "image/png") + .header("Content-Disposition", "attachment; filename=\"" + downloadFileName + "\"") + .body(resource); + } + + + /** + * 获得项目文件中的MP4 + * @param colonPath + * @return + * @throws UnsupportedEncodingException + */ + @RequestMapping("/getmp42/{colonPath}/{img}") + public ResponseEntity getmp4(@PathVariable String colonPath,@PathVariable String img) throws IOException { + String decodedPath = URLDecoder.decode(colonPath, "UTF-8"); + img = URLDecoder.decode(img, "UTF-8"); + + Project byName = projectPerService.getByName(decodedPath); + + if (byName==null)return null; + String imgPath = byName.getImgPath(); + + String file = "/root/rindro/Script/Project/"+imgPath+"/vid/"+img+".mp4"; + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + file = "C:\\Users\\12134\\Desktop\\工作目录\\rindro\\Script\\Project\\"+imgPath+"\\vid\\"+img+".mp4"; + } + // 5. 返回文件 + Resource resource = new FileSystemResource(file); + if (!resource.exists()) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok() + .header("Content-Type", "video/mp4") + // 添加下行强制变为下载行为(与Alist一致) + .header("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"") + .header("Accept-Ranges", "bytes") + .body(resource); + } + + + //购买 + @PostMapping("/buy") + public ResponseEntity> buy( @RequestBody Map body,@CurrentUserId String userName) throws Exception { + + + String buy = userService.buy(body.get("ip"), body.get("projectName"), userName); + Map result = new HashMap<>(); + result.put("info",buy); + if (buy.equals("购买成功")){ + UserServerProject byIPAndName = userServerProjectService.getByIPAndName(body.get("ip"), body.get("projectName")); + DateTime date = DateUtil.date(byIPAndName.getExpirationDate()); + + String format = DateUtil.format(date, "yyyy-MM-dd"); + result.put("endTime",format); + return ResponseEntity.ok(CommonResult.success(result)); + } + + + return ResponseEntity.ok(CommonResult.failed(buy)); + } + + //充值 + @GetMapping("/addgold") + public ResponseEntity> addgold(String userName,Integer gold){ + String addgold = userService.addgold(userName,gold); + + return ResponseEntity.ok(CommonResult.success(addgold)); + } + //转让 + @PostMapping("/transfer") + public ResponseEntity> transfer(@RequestBody Map body,@CurrentUserId String userName){ + String transfer = userService.transfer(userName,body.get("username"), Integer.valueOf(body.get("gold"))); + + return ResponseEntity.ok(CommonResult.success(transfer)); + } + + + //给账号添加服务器 + @PostMapping("/addServer") + public ResponseEntity> addServer(@RequestBody Map body,@CurrentUserId String userName){ + String addServer = userService.addServer(body.get("ip"),userName,body.get("username"),body.get("password"),body.get("port")); + + return ResponseEntity.ok(CommonResult.success(addServer)); + } + //给账号删除服务器 + @PostMapping("/deleteServer") + public ResponseEntity> deleteServer(@RequestBody Map body,@CurrentUserId String userName){ + String deleteServer = userService.deleteServer(body.get("ip"), userName); + + return ResponseEntity.ok(CommonResult.success(deleteServer)); + } + //获得服务器列表 + @PostMapping("/getServerList") + public ResponseEntity>>> getServerList(@CurrentUserId String userName){ + List serverList = userAndServerService.getServerList(userName); +// List collect = serverList.stream().map(UserAndServer::getServerIp).collect(Collectors.toList()); + List > body = serverList.stream().map(item -> { + Map map = new HashMap<>(); + map.put("serverIp", item.getServerIp()); + map.put("userName", item.getAccount()); + map.put("password", item.getPassword()); + map.put("port", item.getPort()); + return map; + } + ).collect(Collectors.toList()); + + + return ResponseEntity.ok(CommonResult.success(body)); + } + + //获得服务器信息 + @PostMapping("/getServer") + public ResponseEntity> getServer(@RequestBody Map body,@CurrentUserId String userName){ + + UserAndServer userAndServer = userAndServerService.getOne(new QueryWrapper().eq("user_name", userName).eq("server_ip", body.get("ip"))); + + return ResponseEntity.ok(CommonResult.success(userAndServer)); + } + + //获取插件信息 + @PostMapping("/getProjectInfoList") + public ResponseEntity> getProjectInfoList(){ + Map> body = projectPerService.getProjectInfoList(); + + return ResponseEntity.ok(CommonResult.success(body)); + } + + /** + * 获取自己拥有的插件信息 + * @return + */ + @PostMapping("/getUserProList") + public ResponseEntity> getgetUserProList(@RequestBody Map body) { + List byUserName = userServerProjectService.getpro(body.get("ip")); + Map> body2 = projectPerService.getProjectInfoListAll(); + + Map> re =new HashMap<>(); + for (UserServerProject userServerProject : byUserName) { + Map objectMap = body2.get(userServerProject.getProjectName()); + DateTime date = DateUtil.date(userServerProject.getExpirationDate()); + + String format = DateUtil.format(date, "yyyy-MM-dd"); + objectMap.put("endTime",format); + objectMap.put("open",userServerProject.getOpen()); + re.put(userServerProject.getProjectName(),objectMap); + } + return ResponseEntity.ok(CommonResult.success(re)); + } + + + @GetMapping("/download/sh") + public ResponseEntity downloadFileSh() { + // 构建文件的完整路径 + File file = new File(fileManage.getFile()+"re.sh"); + + StaticLog.info("请求下载sh脚本"+file); + if (!file.exists()) { + // 如果文件不存在,返回 404 错误 + return ResponseEntity.notFound().build(); + } + + // 创建文件资源对象 + Resource resource = new FileSystemResource(file); + + // 设置响应头,告知浏览器这是一个附件,需要下载 + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + "re.sh"); + + // 返回响应实体,包含文件资源、响应头和状态码 + return ResponseEntity.ok() + .headers(headers) + .contentLength(file.length()) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(resource); + } + + @GetMapping("/download/c") + public ResponseEntity downloadFileC() { + // 构建文件的完整路径 + return getResourceResponseEntity("download/ndpsm_svr", "ndpsm_svr"); + } + + @GetMapping("/download/d") + public ResponseEntity downloadFileD() { + // 构建文件的完整路径 + return getResourceResponseEntity("download/DPS_Login_Gateway", "DPS_Login_Gateway"); + } + + + @GetMapping("/download/djson") + public ResponseEntity downloadFileDjson() { + return getResourceResponseEntity("download/config.json", "config.json"); + } + + @GetMapping("/download/dsh") + public ResponseEntity downloadFileDsh() { + return getResourceResponseEntity("download/DpsLoginRestartSh", "DpsLoginRestartSh"); + } + @GetMapping("/download/ddsh") + public ResponseEntity downloadFileDrsh() { + return getResourceResponseEntity("download/DpsLoginDownloadSh", "DpsLoginDownloadSh"); + } + + private ResponseEntity getResourceResponseEntity(String x, String x1) { + // 构建文件的完整路径 + File file = new File(fileManage.getFile() + x); + if (!file.exists()) { + // 如果文件不存在,返回 404 错误 + return ResponseEntity.notFound().build(); + } + + // 创建文件资源对象 + Resource resource = new FileSystemResource(file); + + // 设置响应头,告知浏览器这是一个附件,需要下载 + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + x1); + + // 返回响应实体,包含文件资源、响应头和状态码 + return ResponseEntity.ok() + .headers(headers) + .contentLength(file.length()) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(resource); + } + + + @GetMapping("/download/jdk") + public ResponseEntity downloadFileJdk() { + // 构建文件的完整路径 + return getResourceResponseEntity("jdk", "ndpsm_svr"); + } + + @Autowired + private RateLimiter globalRateLimiter; + + @GetMapping("/download/jar") + public ResponseEntity downloadFilejar(HttpServletResponse response) { + File file = new File(fileManage.getFile()+"download/RT.tar.gz"); + if (!file.exists()) { + return ResponseEntity.notFound().build(); + } + + try (InputStream inputStream = Files.newInputStream(file.toPath()); + BufferedInputStream bis = new BufferedInputStream(inputStream); + OutputStream os = response.getOutputStream()) { + + // 设置响应头 + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=RT.tar.gz"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + response.setContentLengthLong(file.length()); + + byte[] buffer = new byte[1024 * 8]; // 8KB 缓冲区 + int bytesRead; + + // 动态限速:每次读取后根据数据量申请令牌 + while ((bytesRead = bis.read(buffer)) != -1) { + // 申请令牌(阻塞直到获取足够令牌) + globalRateLimiter.acquire(bytesRead); + os.write(buffer, 0, bytesRead); + os.flush(); + } + + } catch (IOException e) { + return ResponseEntity.status(500).build(); + } + return ResponseEntity.ok().build(); + } + + + @PostMapping("/download/all") + public ResponseEntity downloadFileall(@RequestBody Map body,HttpServletResponse response) { + String s = body.get("key"); + + FileReader fileReader = new FileReader(fileManage.getFilemap()); + String result = fileReader.readString(); + JSONObject jsonObject = JSONUtil.parseObj(result); + String str = jsonObject.getStr(s); + + File file = new File(str); + StaticLog.info("请求下载文件"+file); + + if (!file.exists()) { + return ResponseEntity.notFound().build(); + } + + try (InputStream inputStream = Files.newInputStream(file.toPath()); + BufferedInputStream bis = new BufferedInputStream(inputStream); + OutputStream os = response.getOutputStream()) { + + // 设置响应头 + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=RT.tar.gz"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + response.setContentLengthLong(file.length()); + + byte[] buffer = new byte[1024 * 8]; // 8KB 缓冲区 + int bytesRead; + + // 动态限速:每次读取后根据数据量申请令牌 + while ((bytesRead = bis.read(buffer)) != -1) { + // 申请令牌(阻塞直到获取足够令牌) + globalRateLimiter.acquire(bytesRead); + os.write(buffer, 0, bytesRead); + os.flush(); + } + } catch (IOException e) { + return ResponseEntity.status(500).build(); + } + return ResponseEntity.ok().build(); + } + + @RequestMapping("/download/gr") + public ResponseEntity downloadFilegr(String body,HttpServletResponse response) { + + FileReader fileReader = new FileReader(fileManage.getFilemap()); + String result = fileReader.readString(); + JSONObject jsonObject = JSONUtil.parseObj(result); + String str = jsonObject.getStr(body); + + File file = new File(str); + StaticLog.info("请求下载文件"+file); + + if (!file.exists()) { + return ResponseEntity.notFound().build(); + } + + try (InputStream inputStream = Files.newInputStream(file.toPath()); + BufferedInputStream bis = new BufferedInputStream(inputStream); + OutputStream os = response.getOutputStream()) { + + // 设置响应头 + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+file.getName()); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + response.setContentLengthLong(file.length()); + + byte[] buffer = new byte[1024 * 8]; // 8KB 缓冲区 + int bytesRead; + + // 动态限速:每次读取后根据数据量申请令牌 + while ((bytesRead = bis.read(buffer)) != -1) { + // 申请令牌(阻塞直到获取足够令牌) + globalRateLimiter.acquire(bytesRead); + os.write(buffer, 0, bytesRead); + os.flush(); + } + } catch (IOException e) { + return ResponseEntity.status(500).build(); + } + return ResponseEntity.ok().build(); + } + + + @RequestMapping("/getversion") + public String getversion() { + + FileReader fileReader = new FileReader(fileManage.getFile()+"download/version"); + String result = fileReader.readString(); + return result; + } + + @GetMapping("/getmp4/{colonPath}/{img}") + public ResponseEntity streamVideo( + @RequestHeader(value = "Range", required = false) String rangeHeader, @PathVariable String colonPath,@PathVariable String img) { + + try { + String decodedPath = URLDecoder.decode(colonPath, "UTF-8"); + img = URLDecoder.decode(img, "UTF-8"); + + Project byName = projectPerService.getByName(decodedPath); + + if (byName==null)return null; + String imgPath = byName.getImgPath(); + + Path videoPath = Paths.get("/root/rindro/Script/Project/"+imgPath+"/vid/"+img+".mp4"); + + + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + videoPath = Paths.get("C:\\Users\\12134\\Desktop\\工作目录\\rindro\\Script\\Project\\"+imgPath+"\\vid\\"+img+".mp4"); + } + + Resource videoResource = new UrlResource(videoPath.toUri()); + + if (!videoResource.exists() || !videoResource.isReadable()) { + return ResponseEntity.notFound().build(); + } + + long contentLength = videoResource.contentLength(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.parseMediaType("video/mp4")); + headers.set("Accept-Ranges", "bytes"); + + if (rangeHeader != null && rangeHeader.startsWith("bytes=")) { + String[] ranges = rangeHeader.substring(6).split("-"); + long start = Long.parseLong(ranges[0]); + long end = (ranges.length > 1 && !ranges[1].isEmpty()) + ? Long.parseLong(ranges[1]) + : contentLength - 1; + long rangeLength = end - start + 1; + + if (start >= contentLength || end >= contentLength) { + return ResponseEntity.status(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE) + .header("Content-Range", "bytes */" + contentLength) + .build(); + } + + // 获取输入流并跳过起始字节 + InputStream inputStream = videoResource.getInputStream(); + long skippedBytes = inputStream.skip(start); + if (skippedBytes != start) { + throw new IOException("无法跳过指定字节数"); + } + + headers.set("Content-Range", "bytes " + start + "-" + end + "/" + contentLength); + headers.setContentLength(rangeLength); + + return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT) + .headers(headers) + .body(new InputStreamResource(inputStream)); + } else { + return ResponseEntity.ok() + .headers(headers) + .body(videoResource); + } + } catch (Exception e) { + return ResponseEntity.internalServerError().build(); + } + } + + //根据ip获取插件列表 + @PostMapping("/getServerProList") + public ResponseEntity>> getServerList(@RequestBody Map body, @CurrentUserId String userName){ + if (userAndServerService.selectCount(body.get("ip"),userName)==0){ + return ResponseEntity.ok(CommonResult.success(null)); + } + List ip = userServerProjectService.getpro(body.get("ip")); + return ResponseEntity.ok(CommonResult.success(ip)); + } + + //设置用户单机用的key值 + @PostMapping("/setKey") + public ResponseEntity> setKey(@RequestBody Map body, @CurrentUserId String userName){ + + String s = userService.setKey(userName, body.get("clientkey"), body.get("serverkey")); + + return ResponseEntity.ok(CommonResult.success(s)); + } + + @PostMapping("/setproopen") + public ResponseEntity> setproOpen(@RequestBody Map body, @CurrentUserId String userName){ + String s = userServerProjectService.setproOpen(body.get("ip"), body.get("projectName"), body.get("open"),userName); + return ResponseEntity.ok(CommonResult.success(s)); + } + + + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/controller/UserServerProjectController.java b/旧的java项目/src/main/java/com/example/sjkbf/controller/UserServerProjectController.java new file mode 100644 index 0000000..7432cae --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/controller/UserServerProjectController.java @@ -0,0 +1,22 @@ +package com.example.sjkbf.controller; + +import org.springframework.web.bind.annotation.RequestMapping; + +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 前端控制器 + *

+ * + * @author bozhiqiang + * @since 2025-04-19 + */ +@RestController +@RequestMapping("/user-server-project") +public class UserServerProjectController { + + + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/controller/VideoController.java b/旧的java项目/src/main/java/com/example/sjkbf/controller/VideoController.java new file mode 100644 index 0000000..1c68294 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/controller/VideoController.java @@ -0,0 +1,344 @@ +package com.example.sjkbf.controller; + +import com.example.sjkbf.entity.projectconfig.Project; +import com.example.sjkbf.service.ProjectPerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.*; +import org.springframework.http.*; +import org.springframework.web.bind.annotation.*; +import java.io.*; +import java.net.URLDecoder; +import java.nio.file.*; +import java.text.DecimalFormat; +import java.util.concurrent.*; + +@RestController +@RequestMapping("/api") +public class VideoController { + + private static final String VIDEO_ROOT = "/root/rindro/Script/Project"; + private static final String TRANSCODE_ROOT = "/tmp/video-transcoded"; + + private final ExecutorService transcodeExecutor = Executors.newFixedThreadPool(4); + + @Autowired + private ProjectPerService projectPerService; + private final DecimalFormat durationFormat = new DecimalFormat("0.00"); + + @GetMapping("/videos/{project}/{videoName}") + public ResponseEntity streamVideo( + @PathVariable String project, + @PathVariable String videoName, + @RequestHeader(value = "User-Agent", required = false) String userAgent, + @RequestHeader(value = "Range", required = false) String rangeHeader) throws Exception { + + System.out.println("原始参数 - project: " + project + ", videoName: " + videoName); + +// project ="project"; +// videoName = "0"; + // 直接使用 Spring 已经解码的参数 + project = projectPerService.getByName(project).getImgPath(); + +// project = projectPerService.getByName(decodedPath).getImgPath(); + + Path sourcePath = Paths.get(VIDEO_ROOT, project, "vid", videoName + ".mp4"); + + // 检查源文件是否存在 + if (!Files.exists(sourcePath)) { + System.out.println("源文件不存在: " + sourcePath); + return ResponseEntity.notFound().build(); + } + + boolean needsTranscode = needsTranscode(sourcePath, userAgent); + + if (needsTranscode) { + Path transcodedPath = getTranscodedPath(project, videoName); + + // 如果转码文件不存在或正在转码,先返回原始文件 + if (!Files.exists(transcodedPath) || isTranscodingInProgress(transcodedPath)) { + // 异步触发转码(如果还没开始的话) + if (!Files.exists(transcodedPath)) { + startTranscode(project, videoName); + } + // 返回原始文件作为降级方案 + return buildVideoResponse(sourcePath, rangeHeader, false); + } + + return buildVideoResponse(transcodedPath, rangeHeader, true); + } else { + return buildVideoResponse(sourcePath, rangeHeader, false); + } + } + + /** + * 改进的转码检测逻辑 + */ + private boolean needsTranscode(Path videoPath, String userAgent) throws IOException { + if (!Files.exists(videoPath)) { + throw new FileNotFoundException("视频文件不存在: " + videoPath); + } + + // 不再强制为移动设备转码,而是基于实际编码格式判断 + try { + // 检测视频编码 + String videoCodec = getStreamCodec(videoPath, "v"); + String audioCodec = getStreamCodec(videoPath, "a"); + + // 需要转码的情况: + // 1. 视频不是H.264 + // 2. 音频不是AAC或MP3 + boolean needTranscode = !"h264".equalsIgnoreCase(videoCodec) || + (!"aac".equalsIgnoreCase(audioCodec) && !"mp3".equalsIgnoreCase(audioCodec)); + + System.out.println("视频编码: " + videoCodec + ", 音频编码: " + audioCodec + ", 需要转码: " + needTranscode); + return needTranscode; + + } catch (Exception e) { + // 如果检测失败,默认需要转码 + System.err.println("编码检测失败,默认需要转码: " + e.getMessage()); + return true; + } + } + + /** + * 获取音视频流编码 + */ + private String getStreamCodec(Path videoPath, String streamType) throws IOException { + Process proc = new ProcessBuilder( + "ffprobe", + "-v", "error", + "-select_streams", streamType + ":0", + "-show_entries", "stream=codec_name", + "-of", "default=noprint_wrappers=1:nokey=1", + videoPath.toString() + ).start(); + + try (BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()))) { + String codec = br.readLine(); + return codec != null ? codec.trim() : "unknown"; + } + } + + /** + * 获取转码路径并触发转码 + */ + private Path getTranscodedPath(String project, String videoName) { + return Paths.get(TRANSCODE_ROOT, project, videoName + "_h264.mp4"); + } + + /** + * 启动转码任务 + */ + private void startTranscode(String project, String videoName) { + transcodeExecutor.submit(() -> transcodeVideo(project, videoName)); + } + + /** + * 改进的转码方法 - 使用更兼容的参数 + */ + private void transcodeVideo(String project, String videoName) { + Path transcodedPath = getTranscodedPath(project, videoName); + Path tempPath = Paths.get(transcodedPath.toString() + ".tmp"); + + try { + // 确保目录存在 + Files.createDirectories(transcodedPath.getParent()); + + String source = Paths.get(VIDEO_ROOT, project, "vid", videoName + ".mp4").toString(); + + // 简化转码命令 - 提高兼容性 + String[] cmd = { + "ffmpeg", "-i", source, + "-c:v", "libx264", + "-preset", "medium", + "-crf", "23", + "-profile:v", "high", // 改为high提高质量 + "-level", "4.0", + "-c:a", "aac", + "-b:a", "128k", + "-movflags", "+faststart", // 只保留faststart + "-y", tempPath.toString() // 先输出到临时文件 + }; + + System.out.println("执行转码命令: " + String.join(" ", cmd)); + + ProcessBuilder pb = new ProcessBuilder(cmd); + pb.redirectErrorStream(true); + Process p = pb.start(); + + // 读取输出用于调试 + try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + System.out.println("FFmpeg: " + line); + } + } + + int exitCode = p.waitFor(); + + if (exitCode == 0) { + // 转码成功,重命名临时文件 + Files.move(tempPath, transcodedPath, StandardCopyOption.REPLACE_EXISTING); + System.out.println("转码完成: " + transcodedPath); + } else { + System.err.println("转码失败,退出码: " + exitCode); + Files.deleteIfExists(tempPath); + } + + } catch (Exception e) { + System.err.println("转码过程出错: " + e.getMessage()); + e.printStackTrace(); + try { + Files.deleteIfExists(tempPath); + } catch (IOException ioException) { + ioException.printStackTrace(); + } + } + } + + /** + * 检查是否正在转码 + */ + private boolean isTranscodingInProgress(Path transcodedPath) { + Path tempPath = Paths.get(transcodedPath.toString() + ".tmp"); + return Files.exists(tempPath); + } + + /** + * 检查原始文件是否比转码文件新 + */ + private boolean isSourceNewer(String project, String videoName, Path transcodedPath) throws IOException { + Path sourcePath = Paths.get(VIDEO_ROOT, project, "vid", videoName + ".mp4"); + return Files.getLastModifiedTime(sourcePath).toInstant() + .isAfter(Files.getLastModifiedTime(transcodedPath).toInstant()); + } + + /** + * 构建视频响应 + */ + private ResponseEntity buildVideoResponse(Path videoPath, String rangeHeader, boolean isTranscoded) + throws IOException { + + if (!Files.exists(videoPath)) { + return ResponseEntity.notFound().build(); + } + + Resource resource = new FileSystemResource(videoPath); + long fileLength = resource.contentLength(); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Accept-Ranges", "bytes"); + headers.set("Content-Disposition", "inline"); + + // 处理范围请求 + if (rangeHeader != null && rangeHeader.startsWith("bytes=")) { + String range = rangeHeader.substring(6); + String[] ranges = range.split("-"); + + long start = Long.parseLong(ranges[0]); + long end = ranges.length > 1 ? Long.parseLong(ranges[1]) : fileLength - 1; + long rangeLength = end - start + 1; + + if (rangeLength < 0 || start < 0 || end >= fileLength) { + return ResponseEntity.status(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE) + .header("Content-Range", "bytes */" + fileLength) + .build(); + } + + headers.set("Content-Range", "bytes " + start + "-" + end + "/" + fileLength); + headers.setContentLength(rangeLength); + + // 读取部分内容 + InputStreamResource inputStreamResource = createPartialContent(resource, start, rangeLength); + + return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT) + .headers(headers) + .contentType(MediaType.parseMediaType("video/mp4")) + .body(inputStreamResource); + } + + // 完整文件响应 + headers.setContentLength(fileLength); + headers.set("Content-Type", "video/mp4"); + headers.set("X-Content-Duration", getVideoDuration(videoPath)); + headers.set("Cache-Control", isTranscoded ? "public, max-age=2592000" : "no-cache"); + + return ResponseEntity.ok() + .headers(headers) + .body(resource); + } + + /** + * 创建部分内容资源 + */ + private InputStreamResource createPartialContent(Resource resource, long start, long length) throws IOException { + InputStream inputStream = resource.getInputStream(); + inputStream.skip(start); + + return new InputStreamResource(inputStream) { + @Override + public InputStream getInputStream() throws IOException { + return new LimitedInputStream(inputStream, length); + } + + @Override + public long contentLength() throws IOException { + return length; + } + }; + } + + /** + * 限制输入流 + */ + private static class LimitedInputStream extends InputStream { + private final InputStream wrapped; + private long remaining; + + public LimitedInputStream(InputStream wrapped, long limit) { + this.wrapped = wrapped; + this.remaining = limit; + } + + @Override + public int read() throws IOException { + if (remaining <= 0) return -1; + int result = wrapped.read(); + if (result != -1) remaining--; + return result; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (remaining <= 0) return -1; + int toRead = (int) Math.min(len, remaining); + int result = wrapped.read(b, off, toRead); + if (result != -1) remaining -= result; + return result; + } + } + + /** + * 获取视频时长 + */ + private String getVideoDuration(Path videoPath) throws IOException { + try { + Process proc = new ProcessBuilder( + "ffprobe", "-v", "error", + "-show_entries", "format=duration", + "-of", "default=noprint_wrappers=1:nokey=1", + videoPath.toString() + ).start(); + + try (BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()))) { + String duration = br.readLine(); + if (duration != null && !duration.trim().isEmpty()) { + return durationFormat.format(Double.parseDouble(duration.trim())); + } + } + } catch (Exception e) { + System.err.println("获取视频时长失败: " + e.getMessage()); + } + return "0.00"; + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/controller/ZfController.java b/旧的java项目/src/main/java/com/example/sjkbf/controller/ZfController.java new file mode 100644 index 0000000..1409630 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/controller/ZfController.java @@ -0,0 +1,73 @@ +package com.example.sjkbf.controller; + + +import cn.hutool.core.util.IdUtil; +import cn.hutool.crypto.digest.DigestUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.example.sjkbf.config.Configuration.CurrentUserId; +import com.example.sjkbf.entity.CommonResult; +import com.example.sjkbf.service.UserOrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.util.Map; +@RestController() +@RequestMapping("/zf") + +public class ZfController { + + String payKey = "8658ea66fbbe4a1aad4691ddecefe5fa"; + + String payUrl = "https://2277883.pay.lanjingzf.com/createOrder?"; + + + @Autowired + private UserOrderService service; + + /** + * + * @param payId 商户订单号 + * @param param 创建订单的时候传入的参数 + * @param type 支付方式 :微信支付为1 支付宝支付为2 + * @param price 订单金额 + * @param reallyPrice 实际支付金额 + * @param sign 校验签名,计算方式 = md5(payId + param + type + price + reallyPrice + 通讯密钥) + */ + @RequestMapping("callback") + public String callback(@RequestParam String payId,@RequestParam String param,@RequestParam Integer type, @RequestParam Float price,@RequestParam Float reallyPrice,@RequestParam String sign){ + + System.out.println(payId); + System.out.println(param); + System.out.println(type); + System.out.println(price); + System.out.println(reallyPrice); + System.out.println(sign); + + + + return service.callback(payId, param, type, price, reallyPrice, sign); + } + + @RequestMapping("create") + public ResponseEntity> create(@RequestBody Map body, @CurrentUserId String userName) throws IOException { + + String s = service.create(body.get("type"), body.get("price"), userName); + + return ResponseEntity.ok( CommonResult.success(s)); + + } + + public static void main(String[] args) throws InterruptedException, IOException { + UserOrderService service = new UserOrderService(); + String s = service.create(1, 1, "123"); + System.out.println(s); + } +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/controller/verifyController.java b/旧的java项目/src/main/java/com/example/sjkbf/controller/verifyController.java new file mode 100644 index 0000000..f0c77dd --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/controller/verifyController.java @@ -0,0 +1,729 @@ +package com.example.sjkbf.controller; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.file.FileReader; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import cn.hutool.crypto.symmetric.SymmetricAlgorithm; +import cn.hutool.crypto.symmetric.SymmetricCrypto; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.hutool.log.StaticLog; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.example.sjkbf.common.FileManage; +import com.example.sjkbf.entity.CommonResult; +import com.example.sjkbf.entity.DPSScriptJson; +import com.example.sjkbf.entity.User; +import com.example.sjkbf.entity.UserServerProject; +import com.example.sjkbf.entity.projectconfig.ProjectConfig; +import com.example.sjkbf.service.ProjectPerService; +import com.example.sjkbf.service.UserServerProjectService; +import com.example.sjkbf.service.UserService; +import com.example.sjkbf.service.impl.ProjectPerServiceImpl; +import com.example.sjkbf.util.RSATooL; +import com.example.sjkbf.util.SimpleNutZipCompatible; +import com.example.sjkbf.util.tool; +import org.checkerframework.checker.units.qual.A; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +@RestController +@RequestMapping("/script") +public class verifyController { + + @Autowired + private UserServerProjectService userServerProjectService; + + @Autowired + private ProjectPerService projectPerService; + + @Autowired + private FileManage fileManage; + + @Autowired + private UserService userService; + + + public static Map ipAndMac=new HashMap<>(); + + + + @RequestMapping("client") + public ResponseEntity client(String ip,String clientKey,String ve) { + + if (ve == null )ve = "1.0"; + System.out.println( ve); + StaticLog.info(ip+" client 客户端 请求验证"); + + List getpro=new ArrayList<>(); +// if (ip.equals("192.168.200.131")){ +// User clientKey1 = userService.getOne(new QueryWrapper().eq("client_key", clientKey)); +// +// getpro = userServerProjectService.getByUserName(clientKey1.getName()); +// +// }else { + + getpro = userServerProjectService.getproAndOpen(ip); +// } + + int[] key = new int[20]; + StringBuilder keys= new StringBuilder(); + for (int i = 0; i < 20; i++) { + key[i]=(RandomUtil.randomInt(1, 9)); + keys.append(key[i]); + } + + + + + List BaseScriptStr = projectPerService.getBaseScriptStrEncrypt(key,ve); + + + //如果项目里有cs 就发测试base + for (UserServerProject userServerProject : getpro) { + if (userServerProject.getProjectName().equals("cs")){ + BaseScriptStr = projectPerService.getCSBaseScriptStrEncrypt(key,ve); + break; + } + } + + if (!getpro.isEmpty()) { + for (UserServerProject userServerProject : getpro) { + String projectName = userServerProject.getProjectName(); + if (projectName.equals("cs"))continue; + + List projectScriptStrEncrypt = projectPerService.getProjectScriptStrEncrypt(projectName, key,ve); + BaseScriptStr.addAll(projectScriptStrEncrypt); + System.out.println(projectName + " 加载" + projectScriptStrEncrypt.size() + "个文件"); + + } + } + + System.out.println(BaseScriptStr.size()); + List projectScriptStrEncrypt = projectPerService.getzdy(ip, key,ve); + if (!projectScriptStrEncrypt.isEmpty()) { + BaseScriptStr.addAll(projectScriptStrEncrypt); + System.out.println("加载自定义脚本" + projectScriptStrEncrypt.size() + "个文件"); + } + + + System.out.println(BaseScriptStr.size()); + + + StringBuilder stringBuilder = new StringBuilder(); + for (String s : BaseScriptStr) { + + + stringBuilder.append(s); + stringBuilder.append("$$$$$"); + } + + + + + JSONObject json = new JSONObject(); + json.set("getBaseScriptStr",stringBuilder.toString()); + json.set("key", RSATooL.encryption(keys.toString())); + String string = json.toString(); + String compress = RSATooL.compress(string); + + + return ResponseEntity.ok(compress); + } + + + @RequestMapping("getzdy") + public String getzdy( HttpServletRequest request,String ve) { + String ipAddress = request.getRemoteAddr(); + // 如果应用部署在反向代理或负载均衡器后面,可以通过X-Forwarded-For头部获取真实IP地址 + String forwardedFor = request.getHeader("X-Forwarded-For"); + if (forwardedFor != null && !forwardedFor.isEmpty()) { + ipAddress = forwardedFor.split(",")[0]; + } + + + System.out.println(ipAddress + "请求自定义脚本"); + + List projectScriptStrEncrypt = projectPerService.getzdy2(ipAddress,ve); + if (!projectScriptStrEncrypt.isEmpty()) { + System.out.println("加载自定义脚本" + projectScriptStrEncrypt.size() + "个文件"); + }else { + return ""; + } + + +// StringBuilder stringBuilder = new StringBuilder(); +// for (String s : projectScriptStrEncrypt) { +// +// +// stringBuilder.append(s); +// stringBuilder.append("$$$$$"); +// } + + JSONObject json = new JSONObject(); + json.set("getBaseScriptStr",projectScriptStrEncrypt); + String string = json.toString(); + +// String compress = RSATooL.compress(string);//压缩 + + + return string; + } + + + @RequestMapping("client2") + public ResponseEntity> client2(String ip,String clientKey,String ve) { + System.out.println( ve); + StaticLog.info(ip+"client2 客户端 请求验证"); + + List getpro=new ArrayList<>(); +// if (ip.equals("192.168.200.131")){ +// User clientKey1 = userService.getOne(new QueryWrapper().eq("client_key", clientKey)); +// +// getpro = userServerProjectService.getByUserName(clientKey1.getName()); +// +// }else { + + getpro = userServerProjectService.getproAndOpen(ip); +// } + + int[] key = new int[20]; + StringBuilder keys= new StringBuilder(); + for (int i = 0; i < 20; i++) { + key[i]=(RandomUtil.randomInt(1, 9)); + keys.append(key[i]); + } + + List BaseScriptStr = projectPerService.getBaseScriptStrEncrypt2(key,ve); + + if (!getpro.isEmpty()) { + for (UserServerProject userServerProject : getpro) { + String projectName = userServerProject.getProjectName(); + List projectScriptStrEncrypt = projectPerService.getProjectScriptStrEncrypt2(projectName, key,ve); + BaseScriptStr.addAll(projectScriptStrEncrypt); + System.out.println(projectName + " 加载" + projectScriptStrEncrypt.size() + "个文件"); + } + } + + + Map map = new HashMap<>(); + + map.put("getBaseScriptStr",BaseScriptStr); + map.put("key",RSATooL.encryption(keys.toString())); + + return ResponseEntity.ok(map); + } + + + + + /** + * 服务端验证 + */ + @PostMapping("getpro") + public String getpro(HttpServletRequest request, String prot, Long l, String mac, String s){ + + String ipAddress = tool.getIpAddress(request); + + long time = DateUtil.date().getTime(); + + StaticLog.info(ipAddress+"服务端 请求验证"); + + if (Math.abs(time - l)>1000*60*5||mac==null||mac.isEmpty()||prot==null||prot.isEmpty()){ + + StaticLog.info(ipAddress+"服务端 请求验证失败"); + StaticLog.info(String.valueOf(Math.abs(time - l)>1000*60*5)); + StaticLog.info(String.valueOf(mac==null||mac.isEmpty())); + StaticLog.info(String.valueOf(prot==null||prot.isEmpty())); + + return null; + } + + String s1 = ipAndMac.get(ipAddress); + + if (s1==null||s1.isEmpty()){ + ipAndMac.put(ipAddress,mac); + s1 = mac; + } + if (!s1.equals(mac)){ + //不同的md5值 记录日志 + StaticLog.error("md5值和之前不相同 :" + ipAddress); + } + + List getpro = userServerProjectService.getproAndOpen(ipAddress); + if (getpro.isEmpty()) { + StaticLog.error("验证错误" + ipAddress); + return null; + } + StringBuilder pro = new StringBuilder(); + for (UserServerProject userServerProject : getpro) { + pro.append(userServerProject.getProjectName()).append(","); + } + Map body=new HashMap<>(); + body.put("pro", pro.toString()); + body.put("validate",time); + body.put("ce",s); + //生成一个唯一的id 作为穿给中间件的验证值 存到redis 中 + String key = IdUtil.simpleUUID(); + body.put("key",key); + body.put("ip",ipAddress); + + + StaticLog.info(ipAddress+"服务端 通过验证"); + String jsonStr = JSONUtil.toJsonStr(body); + return tool.encryption(jsonStr); + } + + + @RequestMapping("getservice") + public ResponseEntity getservice( HttpServletRequest request,String ser) throws IOException { + StaticLog.info("收到服务器的getservice2请求脚本"); + + // 获取客户端IP地址 + String ipAddress = request.getRemoteAddr(); + + // 如果应用部署在反向代理或负载均衡器后面,可以通过X-Forwarded-For头部获取真实IP地址 + String forwardedFor = request.getHeader("X-Forwarded-For"); + if (forwardedFor != null && !forwardedFor.isEmpty()) { + ipAddress = forwardedFor.split(",")[0]; + } + + + + + int[] intArray1 = new int[20]; + StringBuilder keys= new StringBuilder(); + for (int i = 0; i < 20; i++) { + intArray1[i]=(RandomUtil.randomInt(1, 9)); + keys.append(intArray1[i]); + } + + + //项目map + Map stringStringMap=new HashMap<>(); + List stringStringMapSX=new ArrayList<>(); + + + List getpro = userServerProjectService.getproAndOpen(ipAddress); + + + if (getpro.isEmpty()) { + //把通用的数据线丢到map中 + List strings333 = FileUtil.readUtf8Lines(fileManage.getDpsScriptfile()+"base2/config"); + for (String s : strings333) { + File file2 = FileUtil.file(fileManage.getDpsScriptfile()+"base2/" + s); + FileReader fileReader1 = new FileReader(fileManage.getDpsScriptfile()+"base2/" + s); + + stringStringMap.put(fileManage.getDpsScriptfile()+"base2/" + s, fileReader1.readString().getBytes()); + stringStringMapSX.add(fileManage.getDpsScriptfile()+"base2/" + s); + } + + System.out.println("通过了 没买插件的验证"); + + }else { + + if (ipAddress .equals("112.12.194.152") || ipAddress .equals("202.189.14.166") || ipAddress .equals("45.125.44.2") || ipAddress .equals("110.42.110.159")|| ipAddress .equals("202.189.14.111")){ + //把通用的数据线丢到map中 + List strings333 = FileUtil.readUtf8Lines(fileManage.getDpsScriptfile()+"" + + "" + + "base3/config"); + for (String s : strings333) { + File file2 = FileUtil.file(fileManage.getDpsScriptfile()+"base3/" + s); + FileReader fileReader1 = new FileReader(file2); + + stringStringMap.put(fileManage.getDpsScriptfile()+"base3/" + s, fileReader1 .readString().getBytes()); + stringStringMapSX.add(fileManage.getDpsScriptfile()+"base3/" + s); + } + }else { + //把通用的数据线丢到map中 + List strings333 = FileUtil.readUtf8Lines(fileManage.getDpsScriptfile()+"base/config"); + for (String s : strings333) { + File file2 = FileUtil.file(fileManage.getDpsScriptfile()+"base/" + s); + FileReader fileReader1 = new FileReader(file2); + + stringStringMap.put(fileManage.getDpsScriptfile()+"base/" + s, fileReader1 .readString().getBytes()); + stringStringMapSX.add(fileManage.getDpsScriptfile()+"base/" + s); + } + + } + + + //从数据库找到所有的插件 + for (UserServerProject user2 : getpro) { + String chajian = user2.getProjectName(); + + String s1 = fileManage.getDpsScriptfile() + "project/" + chajian; + Path path = Paths.get(s1); + //根据插件获得服务器的插件路径 然后以路径为key 里面的值为v 存入到map中 之后加密返回 + if (Files.exists(path)) { + List strings444 = FileUtil.readUtf8Lines(s1 + "/config"); + for (String s : strings444) { + File file2 = FileUtil.file(s1 + "/" + s); + FileReader fileReader1 = new FileReader(file2); + stringStringMap.put(s1 + "/" + s, fileReader1.readString().getBytes()); + stringStringMapSX.add(s1 + "/" + s); + } + } + } + + File file2 = FileUtil.file(fileManage.getDpsScriptfile() + "user/通用/main.nut"); + FileReader fileReader1 = new FileReader(file2); + stringStringMap.put(fileManage.getDpsScriptfile() + "user/通用/main.nut", fileReader1.readString().getBytes()); + stringStringMapSX.add(fileManage.getDpsScriptfile() + "user/通用/main.nut"); + } + + if (ipAddress .equals("112.12.194.152") || ipAddress .equals("202.189.14.166") || ipAddress .equals("45.125.44.2") || ipAddress .equals("110.42.110.159")|| ipAddress .equals("202.189.14.111")){ + for (String stringMapSX : stringStringMapSX) { + System.out.println(stringMapSX); + } + } + + + //以下代码为从/root/rindro/dpsScript/user/ 目录中加载自定义的服务端脚本 + String s1 = fileManage.getDpsScriptfile() + "user/" + ipAddress; + Path path = Paths.get(s1); + if (Files.exists(path)) { + List strings444 = FileUtil.readUtf8Lines(s1 + "/config"); + for (String s : strings444) { + File file2 = FileUtil.file(s1 + "/" + s); + FileReader fileReader1 = new FileReader(file2); + stringStringMap.put(s1 + "/" + s, fileReader1.readString().getBytes()); + stringStringMapSX.add(s1 + "/" + s); + } + } + + +// Map stringStringMapUser=new HashMap<>(); +// List stringStringMapUserSX=new ArrayList<>(); + +// try { +// List strings2222 = FileUtil.readUtf8Lines("/root/rindro/user/"+ipAddress + "/serConfig"); +// +// for (String s : strings2222) { +// File file2 = FileUtil.file("/root/rindro/user/"+ipAddress + "/"+s); +// FileReader fileReader = new FileReader(file2); +// byte[] bytes = fileReader.readBytes(); +// stringStringMapUser.put("/root/rindro/user/"+ipAddress + "/"+s,bytes); +// stringStringMapUserSX.add("/root/rindro/user/"+ipAddress + "/"+s); +// } +// }catch (Exception e){ +// System.out.println("未找到userip文件,外部用户请求"); +// } + + + +// 对通用文件进行加密处理 + for (Map.Entry entry : stringStringMap.entrySet()) { + byte[] encryptedValue = RSATooL.Makecode(entry.getValue(), intArray1); + entry.setValue(encryptedValue); // 直接在Map中更新值 + } + + +// //对用户文件进行加密处理 +// for (Map.Entry entry : stringStringMapUser.entrySet()) { +// byte[] encryptedValue = Main.Makecode(entry.getValue(), intArray1); +// entry.setValue(encryptedValue); // 直接在Map中更新值 +// } + + + StringBuilder sb=new StringBuilder(); + for (String string : stringStringMapSX) { + sb.append(string); + sb.append("$$$$$"); + String string1 = Arrays.toString(stringStringMap.get(string)); + + sb.append(string1, 1, string1.length()-1); + + sb.append("$$$$$"); + } + +// for (String string : stringStringMapUserSX) { +// sb.append(string); +// sb.append("$$$$"); +// sb.append(Arrays.toString(stringStringMapUser.get(string))); +// sb.append("$$$$"); +// } + String substring = sb.toString(); + StaticLog.info(ipAddress+"通过验证 返回dps nut脚本"); + JSONObject json = new JSONObject(); + + json.set("getBaseScriptStr",substring); + json.set("key",RSATooL.encryption(keys.toString())); + String string = json.toString(); + String compress = RSATooL.compress(string); + + return ResponseEntity.ok(compress); + } + + Map jsonObject=new HashMap<>(); + @RequestMapping ("getBaseUpdateTime") + public ResponseEntity getBaseUpdateTime(String ve) { + + + if (ve == null) ve = "1.0"; + + // 1. 构建 base 目录路径(避免硬编码拼接,用 File 构造方法更安全) + String baseDirPath = fileManage.getFile() + "/DP-S_Script/Script"+ve+"/_DPS_"; + File baseDir = new File(baseDirPath); + + // 2. 校验目录合法性(不存在/不是目录直接返回 0) + if (!baseDir.exists() || !baseDir.isDirectory()) { + return ResponseEntity.ok("0"); + } + + // 3. 递归遍历所有文件,获取最新修改时间 + long latestModifyTime = traverseAllFiles(baseDir); + + // 4. 返回结果(时间戳转字符串) + return ResponseEntity.ok(String.valueOf(latestModifyTime)); + } + + + @RequestMapping("getBaseSutZip") + public ResponseEntity getBaseSut(HttpServletRequest request,@RequestParam(value = "ve", required = false) String ve) { + nut_sut(ve); + + + String file = fileManage.getFile() +"sut.tar.gz"; + + Path baseDir = Paths.get(file).toAbsolutePath().normalize(); + Resource resource = new FileSystemResource(baseDir); + + // 设置响应头 + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\""); + + // 返回包含文件资源的通用结果 + return ResponseEntity.ok() + .header("Content-Disposition", + "attachment; filename=\"" + resource.getFilename() + "\"") + .body(resource); + } + + Map updateTime=new HashMap<>(); + + + public void nut_sut(String bb) { + if (bb ==null) bb = "1.0"; + String baseDirPath = fileManage.getFile() + "/DP-S_Script/Script"+bb+"/_DPS_"; + File baseDir = new File(baseDirPath); + long latestModifyTime = traverseAllFiles(baseDir); + + System.out.println("最新修改时间:" + latestModifyTime); + System.out.println("updateTime:" + updateTime.get(bb)); + if ( updateTime.get(bb) == null || latestModifyTime > updateTime.get(bb)){ + //重新加密压缩 + + String targetPath = fileManage.getFile()+"sut.tar.gz"; + String key = "Rindro-Aurora"; + SimpleNutZipCompatible.encryptAndZip(baseDirPath, targetPath, key); + + + updateTime.put(bb,traverseAllFiles(baseDir)); + + + FileReader fileReader = new FileReader(fileManage.getFile() + "/DP-S_Script/Script"+bb+"/_DPS_" + "/FileConfig.json"); + String result5 = fileReader.readString(); + //转json + JSONObject jsonObject1 = new JSONObject(result5); + jsonObject.put(bb,jsonObject1); + } + } + + + + @RequestMapping("getservicenew") + public ResponseEntity getservicenew( HttpServletRequest request,String ser,String ve) throws IOException { + + + if (ve == null ){ + ve = request.getHeader("ve"); + if ( ve == null) { + StaticLog.info("版本 为空"); + ve = "1.0"; + } + } + + // 获取客户端IP地址 + String ipAddress = request.getRemoteAddr(); + + + + // 如果应用部署在反向代理或负载均衡器后面,可以通过X-Forwarded-For头部获取真实IP地址 + String forwardedFor = request.getHeader("X-Forwarded-For"); + if (forwardedFor != null && !forwardedFor.isEmpty()) { + ipAddress = forwardedFor.split(",")[0]; + } + StaticLog.info("收到服务器"+ipAddress+" 的getservicenew请求脚本 当前版本" + ve); + + + + int[] intArray1 = new int[20]; + StringBuilder keys= new StringBuilder(); + for (int i = 0; i < 20; i++) { + intArray1[i]=(RandomUtil.randomInt(1, 9)); + keys.append(intArray1[i]); + } + + + //项目map + Map stringStringMap=new HashMap<>(); + List stringStringMapSX=new ArrayList<>(); + + + List getpro = userServerProjectService.getproAndOpen(ipAddress); + + + if (getpro.isEmpty()) { + + return ResponseEntity.ok("没有项目"); + + }else { + + + + //从数据库找到所有的插件 + for (UserServerProject user2 : getpro) { + String chajian = user2.getProjectName(); + if (jsonObject.get(ve) ==null){ + nut_sut(ve); + System.out.println(jsonObject.get(ve)); + } + + DPSScriptJson bean = jsonObject.get(ve).toBean(DPSScriptJson.class); + DPSScriptJson.ProjectScriptItem projectScriptItem = bean.getProjectScript().get(chajian); + if (projectScriptItem == null) { + + StaticLog.info("版本" + ve); + StaticLog.info("没有项目" + chajian); + continue; + } + for (String s : projectScriptItem.getScript()) { + + String s1 = fileManage.getFile() + "/DP-S_Script/Script"+ve+"/_DPS_" + "/_BuiltProject/" + s; + + + File file2 = FileUtil.file(s1 ); + FileReader fileReader1 = new FileReader(file2); + stringStringMap.put(s1 , fileReader1.readString().getBytes()); + stringStringMapSX.add(s1 ); + + } + StaticLog.info("加入" +chajian +"插件"); + + } + +// File file2 = FileUtil.file(fileManage.getDpsScriptfile() + "user/通用/main.nut"); +// FileReader fileReader1 = new FileReader(file2); +// stringStringMap.put(fileManage.getDpsScriptfile() + "user/通用/main.nut", fileReader1.readString().getBytes()); +// stringStringMapSX.add(fileManage.getDpsScriptfile() + "user/通用/main.nut"); + } + + + + //以下代码为从/root/rindro/dpsScript/user/ 目录中加载自定义的服务端脚本 + String s1 = fileManage.getDpsScriptfile() + "user/" + ipAddress; + Path path = Paths.get(s1); + if (Files.exists(path)) { + List strings444 = FileUtil.readUtf8Lines(s1 + "/config"); + for (String s : strings444) { + File file2 = FileUtil.file(s1 + "/" + s); + FileReader fileReader1 = new FileReader(file2); + stringStringMap.put(s1 + "/" + s, fileReader1.readString().getBytes()); + stringStringMapSX.add(s1 + "/" + s); + } + } + + + + +// 对通用文件进行加密处理 + for (Map.Entry entry : stringStringMap.entrySet()) { + byte[] encryptedValue = RSATooL.Makecode(entry.getValue(), intArray1); + entry.setValue(encryptedValue); // 直接在Map中更新值 + } + + + + StringBuilder sb=new StringBuilder(); + for (String string : stringStringMapSX) { + sb.append(string); + sb.append("$$$$$"); + String string1 = Arrays.toString(stringStringMap.get(string)); + + sb.append(string1, 1, string1.length()-1); + + sb.append("$$$$$"); + } + + + String substring = sb.toString(); + StaticLog.info(ipAddress+"通过验证 返回dps nut脚本"); + JSONObject json = new JSONObject(); + + json.set("getBaseScriptStr",substring); + json.set("key",RSATooL.encryption(keys.toString())); + String string = json.toString(); + String compress = RSATooL.compress(string); + + return ResponseEntity.ok(compress); + } + + + + /** + * 递归遍历目录下所有文件(含所有层级子目录),返回最新修改时间 + * @param dir 要遍历的目录 + * @return 最新文件修改时间(毫秒级时间戳),无文件时返回 0 + */ + private long traverseAllFiles(File dir) { + long latestTime = 0; + // 用原生 listFiles() 替代 FileUtil.ls,减少第三方依赖,同时处理 null 情况 + File[] files = dir.listFiles(); + if (files == null) { + return latestTime; // 目录无权限访问或异常,返回当前最新时间 + } + + for (File file : files) { + if (file.isDirectory()) { + // 递归遍历子目录 + latestTime = Math.max(latestTime, traverseAllFiles(file)); + } else { + // 对比文件修改时间,更新最新时间 + latestTime = Math.max(latestTime, file.lastModified()); + } + } + return latestTime; + } + + + public static void main(String[] args) { +// String decompress = RSATooL.encryption("https://dps-ma.senzo.online"); +// System.out.println(decompress); + + System.out.println((3860+1730 -150-180-240-(40+37+94+20)) *0.7) ; + + System.out.println(3380 -100 -381-850-60-50-100-400-100-40-150-250-175 -100); + } + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/entity/AuthUser.java b/旧的java项目/src/main/java/com/example/sjkbf/entity/AuthUser.java new file mode 100644 index 0000000..db0e7db --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/entity/AuthUser.java @@ -0,0 +1,16 @@ +package com.example.sjkbf.entity; + +import lombok.Getter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; +import java.util.Collection; + +@Getter +public class AuthUser extends User { + private final Integer userId; + + public AuthUser(Integer userId, String username, String password, Collection authorities) { + super(username, password, authorities); + this.userId = userId; + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/entity/CommonResult.java b/旧的java项目/src/main/java/com/example/sjkbf/entity/CommonResult.java new file mode 100644 index 0000000..bf3a6a8 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/entity/CommonResult.java @@ -0,0 +1,118 @@ +package com.example.sjkbf.entity; + +import com.baomidou.mybatisplus.extension.api.IErrorCode; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CommonResult { + // 状态码 + private Integer code; + // 提示信息 + private String message; + // 返回的数据 + private T data; + + /** + * 成功返回结果 + * + * @param data 返回的数据 + * @param 数据类型 + * @return 通用返回对象 + */ + public static CommonResult success(T data) { + return new CommonResult<>(200, "操作成功", data); + } + + /** + * 成功返回结果并自定义提示信息 + * + * @param data 返回的数据 + * @param message 提示信息 + * @param 数据类型 + * @return 通用返回对象 + */ + public static CommonResult success(T data, String message) { + return new CommonResult<>(200, message, data); + } + + + /** + * 失败返回结果并自定义提示信息 + * + * @param message 提示信息 + * @param 数据类型 + * @return 通用返回对象 + */ + public static CommonResult failed(String message) { + return new CommonResult<>(500, message, null); + } + + /** + * 失败返回结果 + * + * @param 数据类型 + * @return 通用返回对象 + */ + public static CommonResult failed() { + return failed(500, "操作失败"); + } + + /** + * 失败返回结果 + * + * @param code 错误码 + * @param message 提示信息 + * @param 数据类型 + * @return 通用返回对象 + */ + public static CommonResult failed(int code, String message) { + return new CommonResult<>(code, message, null); + } + + /** + * 参数验证失败返回结果 + * + * @param 数据类型 + * @return 通用返回对象 + */ + public static CommonResult validateFailed() { + return failed(400, "参数验证失败"); + } + + /** + * 参数验证失败返回结果并自定义提示信息 + * + * @param message 提示信息 + * @param 数据类型 + * @return 通用返回对象 + */ + public static CommonResult validateFailed(String message) { + return failed(400, message); + } + + /** + * 未登录返回结果 + * + * @param data 返回的数据 + * @param 数据类型 + * @return 通用返回对象 + */ + public static CommonResult unauthorized(T data) { + return new CommonResult<>(401, "暂未登录或 token 已经过期", data); + } + + /** + * 未授权返回结果 + * + * @param data 返回的数据 + * @param 数据类型 + * @return 通用返回对象 + */ + public static CommonResult forbidden(T data) { + return new CommonResult<>(403, "没有相关权限", data); + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/entity/DPSScriptJson.java b/旧的java项目/src/main/java/com/example/sjkbf/entity/DPSScriptJson.java new file mode 100644 index 0000000..c264f12 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/entity/DPSScriptJson.java @@ -0,0 +1,21 @@ +package com.example.sjkbf.entity; + +import lombok.Data; +import lombok.Getter; + +import java.util.List; +import java.util.Map; +@Data +public class DPSScriptJson { + + + private List BaseScript; + private Map ProjectScript; + + + @Data + public static class ProjectScriptItem { + private List Script; + } + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/entity/DTO/LoginRequest.java b/旧的java项目/src/main/java/com/example/sjkbf/entity/DTO/LoginRequest.java new file mode 100644 index 0000000..8a809b7 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/entity/DTO/LoginRequest.java @@ -0,0 +1,16 @@ +package com.example.sjkbf.entity.DTO; + +import lombok.Data; +import javax.validation.constraints.NotBlank; + +@Data +public class LoginRequest { + @NotBlank(message = "用户名不能为空") + private String username; + + @NotBlank(message = "密码不能为空") + private String password; + + @NotBlank(message = "验证qq不能为空") + private String qq; +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/entity/DTO/LoginResponse.java b/旧的java项目/src/main/java/com/example/sjkbf/entity/DTO/LoginResponse.java new file mode 100644 index 0000000..d752e80 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/entity/DTO/LoginResponse.java @@ -0,0 +1,18 @@ +package com.example.sjkbf.entity.DTO; + +import lombok.Data; + +import java.util.List; + +@Data +public class LoginResponse { + private String token; + private String username; + private List roles; // 权限/角色列表 + + public LoginResponse(String token, String username, List roles) { + this.token = token; + this.username = username; + this.roles = roles; + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/entity/Log.java b/旧的java项目/src/main/java/com/example/sjkbf/entity/Log.java new file mode 100644 index 0000000..c20b64a --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/entity/Log.java @@ -0,0 +1,34 @@ +package com.example.sjkbf.entity; + +import java.io.Serializable; +import java.util.Date; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * + *

+ * + * @author bozhiqiang + * @since 2025-09-25 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class Log implements Serializable { + + private static final long serialVersionUID = 1L; + + + + private String name; + + private String log; + + private Integer type; + //时间 + private Date time; + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/entity/ProjectPer.java b/旧的java项目/src/main/java/com/example/sjkbf/entity/ProjectPer.java new file mode 100644 index 0000000..e39fdbc --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/entity/ProjectPer.java @@ -0,0 +1,31 @@ +package com.example.sjkbf.entity; + +import java.io.Serializable; + +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * + *

+ * + * @author bozhiqiang + * @since 2025-04-15 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class ProjectPer implements Serializable { + + private static final long serialVersionUID = 1L; + @TableId + private String projectName; + + private String file; + + private Integer permission; + + private Integer demandGold; + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/entity/User.java b/旧的java项目/src/main/java/com/example/sjkbf/entity/User.java new file mode 100644 index 0000000..c2279d7 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/entity/User.java @@ -0,0 +1,51 @@ +package com.example.sjkbf.entity; + +import java.time.LocalDateTime; +import java.io.Serializable; +import java.util.Date; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * + *

+ * + * @author bozhiqiang + * @since 2025-04-15 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_user") +public class User implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + private String username; + + private String password; + + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + private Integer gold; + + private String qq; + + private String name; + private String img; + + private String clientKey; + private String serverKey; + + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/entity/UserAndServer.java b/旧的java项目/src/main/java/com/example/sjkbf/entity/UserAndServer.java new file mode 100644 index 0000000..21b74e4 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/entity/UserAndServer.java @@ -0,0 +1,33 @@ +package com.example.sjkbf.entity; + +import java.io.Serializable; + +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * + *

+ * + * @author bozhiqiang + * @since 2025-04-17 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class UserAndServer implements Serializable { + + private static final long serialVersionUID = 1L; + + private String userName; + + private String serverIp; + + private String account; + + private String password; + + private String port; + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/entity/UserServerProject.java b/旧的java项目/src/main/java/com/example/sjkbf/entity/UserServerProject.java new file mode 100644 index 0000000..bd5fa94 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/entity/UserServerProject.java @@ -0,0 +1,33 @@ +package com.example.sjkbf.entity; + +import java.time.LocalDateTime; +import java.io.Serializable; +import java.util.Date; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * + *

+ * + * @author bozhiqiang + * @since 2025-04-19 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class UserServerProject implements Serializable { + + private static final long serialVersionUID = 1L; + + private String serverIp; + + private String projectName; + + private Date expirationDate; + + private String userName; + + private int open; +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/entity/projectconfig/Project.java b/旧的java项目/src/main/java/com/example/sjkbf/entity/projectconfig/Project.java new file mode 100644 index 0000000..c2998d4 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/entity/projectconfig/Project.java @@ -0,0 +1,31 @@ +package com.example.sjkbf.entity.projectconfig; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class Project { + + String name; + + List Script; + + + Map Info; + + /** + * 价格 + */ + Integer price; + + /** + * 是否私有 + */ + Boolean Private = false; + /** + * img路径 + */ + String imgPath; +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/entity/projectconfig/ProjectConfig.java b/旧的java项目/src/main/java/com/example/sjkbf/entity/projectconfig/ProjectConfig.java new file mode 100644 index 0000000..b94a417 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/entity/projectconfig/ProjectConfig.java @@ -0,0 +1,26 @@ +package com.example.sjkbf.entity.projectconfig; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Data +public class ProjectConfig { + + private List BaseScript; + private List CSBaseScript; + + private Map ProjectScript = new HashMap<>(); + + + public List BaseScriptStr = new ArrayList<>(); + public List CSBaseScriptStr = new ArrayList<>(); + + public Map> ProjectScriptStr = new HashMap<>(); + + //自定义脚本 key为ip v为自定义脚本 + public Map> ProjectZDYScriptStr = new HashMap<>(); +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/mapper/LogMapper.java b/旧的java项目/src/main/java/com/example/sjkbf/mapper/LogMapper.java new file mode 100644 index 0000000..cc994f2 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/mapper/LogMapper.java @@ -0,0 +1,18 @@ +package com.example.sjkbf.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.example.sjkbf.entity.Log; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author bozhiqiang + * @since 2025-09-25 + */ +@Mapper +public interface LogMapper extends BaseMapper { + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/mapper/ProjectPerMapper.java b/旧的java项目/src/main/java/com/example/sjkbf/mapper/ProjectPerMapper.java new file mode 100644 index 0000000..45c6462 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/mapper/ProjectPerMapper.java @@ -0,0 +1,18 @@ +package com.example.sjkbf.mapper; + +import com.example.sjkbf.entity.ProjectPer; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author bozhiqiang + * @since 2025-04-15 + */ +@Mapper() +public interface ProjectPerMapper extends BaseMapper { + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/mapper/UserAndServerMapper.java b/旧的java项目/src/main/java/com/example/sjkbf/mapper/UserAndServerMapper.java new file mode 100644 index 0000000..e65a7b1 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/mapper/UserAndServerMapper.java @@ -0,0 +1,18 @@ +package com.example.sjkbf.mapper; + +import com.example.sjkbf.entity.UserAndServer; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author bozhiqiang + * @since 2025-04-17 + */ +@Mapper +public interface UserAndServerMapper extends BaseMapper { + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/mapper/UserMapper.java b/旧的java项目/src/main/java/com/example/sjkbf/mapper/UserMapper.java new file mode 100644 index 0000000..8eb97a5 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/mapper/UserMapper.java @@ -0,0 +1,18 @@ +package com.example.sjkbf.mapper; + +import com.example.sjkbf.entity.User; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author bozhiqiang + * @since 2025-04-15 + */ +@Mapper +public interface UserMapper extends BaseMapper { + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/mapper/UserServerProjectMapper.java b/旧的java项目/src/main/java/com/example/sjkbf/mapper/UserServerProjectMapper.java new file mode 100644 index 0000000..40e2e69 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/mapper/UserServerProjectMapper.java @@ -0,0 +1,18 @@ +package com.example.sjkbf.mapper; + +import com.example.sjkbf.entity.UserServerProject; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author bozhiqiang + * @since 2025-04-19 + */ +@Mapper +public interface UserServerProjectMapper extends BaseMapper { + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/LogService.java b/旧的java项目/src/main/java/com/example/sjkbf/service/LogService.java new file mode 100644 index 0000000..8aadd16 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/service/LogService.java @@ -0,0 +1,16 @@ +package com.example.sjkbf.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.example.sjkbf.entity.Log; + +/** + *

+ * 服务类 + *

+ * + * @author bozhiqiang + * @since 2025-09-25 + */ +public interface LogService extends IService { + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/ProjectPerService.java b/旧的java项目/src/main/java/com/example/sjkbf/service/ProjectPerService.java new file mode 100644 index 0000000..bb9d2df --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/service/ProjectPerService.java @@ -0,0 +1,86 @@ +package com.example.sjkbf.service; + +import com.example.sjkbf.entity.ProjectPer; +import com.baomidou.mybatisplus.extension.service.IService; +import com.example.sjkbf.entity.projectconfig.Project; +import com.example.sjkbf.entity.projectconfig.ProjectConfig; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 服务类 + *

+ * + * @author bozhiqiang + * @since 2025-04-15 + */ +public interface ProjectPerService extends IService { + + /** + * 根据项目名字获得项目信息 + * @param projectName + * @return + */ + Project getByName(String projectName); + + /** + * 获得公开的插件项目信息 + * @return + */ + Map> getProjectInfoList(); + + /** + * 获得全部的插件项目信息 + * @return + */ + Map> getProjectInfoListAll(); + + List getBaseScriptStr( String ve); + + /** + * 获得base加密后的脚本 + * @param key + * @return + */ + List getBaseScriptStrEncrypt(int[] key,String ve); + List getCSBaseScriptStrEncrypt(int[] key,String ve); + + List getProjectScriptStr(String projectName,String ve); + + /** + * 获得项目加密后的脚本 + * @param projectName + * @param key + * @return + */ + List getProjectScriptStrEncrypt(String projectName,int[] key,String ve); + List getProjectScriptStrEncrypt2(String projectName,int[] key,String ve); + + + /** + * 获得项目购买所需积分 + * @param projectName + * @return + */ + int getProjectPrice(String projectName); + + List getBaseScriptStrEncrypt2(int[] key,String ve); + + /** + * 获得自定义脚本 + * @param ip + * @param key + * @return + */ + List getzdy(String ip, int[] key,String ve); + + /** + * 获得没加密的自定义脚本 + * @param ip + * @param ve + * @return + */ + List getzdy2(String ip,String ve); +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/SaticScheduleTask.class b/旧的java项目/src/main/java/com/example/sjkbf/service/SaticScheduleTask.class new file mode 100644 index 0000000000000000000000000000000000000000..d82feaf1c8dab553a2b3a6f299fa69f56d89de16 GIT binary patch literal 3549 zcma)9YjhM>9sk{AcV{yhl8`hlOL;7%w1KirprzD>(k1~~Gy!BOxUGtl+1c!n-I--( zwj}k@s+DSARx9;cUj$ zJNN&%|KI<0XMgjb%U1#1hhM7Lh(Z%=9E)REMG++x4qQ2u<#45$~ z;y5Aa5jl)1cuGYZKCa>u_@o>@C3&A#@eDp4$7kaBY#h(V@i_%2<9IHP&r8Y|6g;os z1r;yiB^9gjvWivsqJmdctVKb-d`Ygp9LKA2<{@8Eu^y)syr$r*0FUC+M3iwp}1`klS|2I&Ay)VA1Mv95*lm z+jV?_2l`!aNcYR0?F{8Tqhy_My1{xHvKAIN?OD1M3i%E(pyZ9p zjm>oXwb!2e^W@mn>39Azetzoev(%rrrT1H^`U!In-zlNyU9V&WQl6gjs;M#8Q?+~{ zbAw?BEXf3hIXqyL!`OxSc~ZgG6nve9q2L<|(h9yAru!?*VjeLAnIYJ^XomUK_%dGg z=`|g7nO+goa~&Suo#dl$Np9ftkU%oTGg2>g!1V*xi63|sGmr=~ZpAY#iC5s(1?jX& z#!TS;MeUOHWQ_#%>Zv+Ry|84ce#fGMiv!y2$egdilF6xIGqx!BmWJ2yZ2@iS-DjrG zzA$x~!Mmd1I~u-=?I z$y7P(`nJ=(xqHi+5tvRhy_-x*>GH)VUY0oj=8zm=BGKst?+Z?iZMnDO1A) z-eG-CgP32yYVTj7DV&cx6kV7IKfhFM(&H+MViq+}|x4m-Q5jikQQ%C>()dmm#*6S$>o{X#jm zt-#G0qnyiz=d(&5sMp?CFuaU)yka?~wSE1~I^fD1Ic$$@6?4q3T|L>O`}Z6j*uU@4 zqXMgMs4wKFCt_|s?^|@$ftpb>(b;|z*}XS4w$^-QcNe&Gf&N8(6*J0Z%OR3fy_0H} z-9m|48z7K);8wYK+0AoOt@5ieik60RHv7h^n*JJ*t`C!K`jr`!vl!AvGvRSo^pF+I zYBfS1hzs3*uY?U*IiE_t~d zXJ5$4?jf-8|8#Ec1E5uYF|m4hF&^>^)503u!%s~>@IQr3oOP~ZoVz(UaAxD@crV{{ z&XO;>VI1OONWPDwWCoUDD@U!$fcvqHZ!8cBH%j4#Un9zIQHa|wp`pET43P;$`zH_^ zxMu>&zSJee+x|U)rgS7hikgm2qB%Pbt$oQDS~l{346W(dB-*lx*f^G^m4q^Z_9OLN ziD;E=0v$(E6SyTEpFn52X`1C$QWG%_?YGHQT&|XJkcw3rl~8GvG%mq8b_L6`jqSH* zvXS-`nQSznX41`6muMct%Ct6#RoO|b&L*@mB*(F49CxIbOkyo9zBApDpv~*D>DFIC zf=#qiU!p}$ceUTm;mFzRf0X+?xr?N;MB6O&Zn#K?oe^!KgWuDQ{1pIxW+ETNW7 zcnA-p1%IV2J?KR%HAK;eU6f#$Ybl_KR_g2}vNq9-JzO=QQ^aWXqr{lRKd~3@M}%1Z zjt`I;CyH8 literal 0 HcmV?d00001 diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/UserAndServerService.java b/旧的java项目/src/main/java/com/example/sjkbf/service/UserAndServerService.java new file mode 100644 index 0000000..76ff85e --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/service/UserAndServerService.java @@ -0,0 +1,25 @@ +package com.example.sjkbf.service; + +import com.example.sjkbf.entity.UserAndServer; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + *

+ * 用户的服务器 服务类 + *

+ * + * @author bozhiqiang + * @since 2025-04-17 + */ +public interface UserAndServerService extends IService { + + int selectCount(String ip, String userName); + + List getServerList(String userName); + + String updateServer(String ip, String userName, String account, String password, String port); + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/UserOrderService.java b/旧的java项目/src/main/java/com/example/sjkbf/service/UserOrderService.java new file mode 100644 index 0000000..f4d299c --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/service/UserOrderService.java @@ -0,0 +1,174 @@ +package com.example.sjkbf.service; + +import cn.hutool.cache.CacheUtil; +import cn.hutool.cache.impl.TimedCache; +import cn.hutool.core.util.IdUtil; +import cn.hutool.crypto.digest.DigestUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.hutool.log.StaticLog; +import com.example.sjkbf.util.MD5; +import lombok.extern.slf4j.Slf4j; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @Author l'amour solitaire + * @Description 订单服务相关 + * @Date 2022/12/1 下午5:24 + **/ +@Slf4j +@Service +public class UserOrderService { + + @Autowired + private UserService userService; + + private final String payKey = "8658ea66fbbe4a1aad4691ddecefe5fa"; + + private final String payUrl = "https://2277883.pay.lanjingzf.com/createOrder?"; + + private static final String CREATE_PARAMS = "payId=%s&type=%s&price=%s&sign=%s&isHtml=1"; + + + + + public static TimedCache timedCache = CacheUtil.newTimedCache(3000); + + Map orderIdAndPrice=new HashMap<>(); + + static { + timedCache.schedulePrune(100); + } + + + + /** + * @param payType 【必传】微信支付传入1 支付宝支付传入2 + * @param price 【必传】订单金额 + * @Author l'amour solitaire + * @Description 创建订单 + * @Date 2022/12/1 下午5:46 + **/ + public String create(Integer payType, int price,String name) throws IOException { + + Integer i = timedCache.get(name); + if (i==null) { + timedCache.put(name, 1,5*60*1000); + + }else { + return "上次提交暂未结束,请稍后重试"; + } + + + + + // 创建订单 订单号为uid:时间戳 + String orderId = name + ":" + System.currentTimeMillis(); + + orderIdAndPrice.put(orderId,price); + + String CREATE_PARAMS = "payId=%s&type=%d&price=%s&sign=%s&isHtml=1"; + + + String signSource = orderId + payType + price + payKey; + String sign = DigestUtil.md5Hex(signSource); + + String paramMap = String.format(CREATE_PARAMS, orderId, payType, price, sign); + + HttpResponse execute = HttpRequest.get(payUrl+paramMap) +// .header(Header.USER_AGENT, "Hutool http")//头信息,多个头信息多次调用此方法即可 + .timeout(20000)//超时,毫秒 + .execute(); + String body = execute.body(); + + + + // 返回支付页面地址,前端进行弹框展示即可 + return "https://2277883.pay.lanjingzf.com/"+extractBetweenQuotes(body); + } + + /** + * 支付回调 + **/ + public String callback(String payId, String param, int type, double price, double reallyPrice, String sign) { + Assert.isTrue(sign.equals(DigestUtil.md5Hex(payId + param + type + price + reallyPrice + payKey)), "非法请求!"); + System.out.println(payId); + Integer i = orderIdAndPrice.get(payId); + + System.out.println(orderIdAndPrice); + if (i==null) { + log.info("订单不存在"); + return "订单不存在"; + } + if (i==-1){ + log.info("订单已处理"); + return "订单已处理"; + } + + String[] split = payId.split(":"); + + + userService.addgold(split[0],i); + log.info("订单支付成功"); + + timedCache.remove(split[0]); + + orderIdAndPrice.put(payId,-1); + + // 返回这个字符串,支付平台就知道回调接口成功了 + return "success"; + } + + + public static String extractBetweenQuotes(String input) { + // 检查输入是否为null + if (input == null) { + return null; + } + + // 找到第一个单引号的位置 + int firstQuoteIndex = input.indexOf('\''); + // 找到第二个单引号的位置(从第一个引号之后开始查找) + int secondQuoteIndex = input.indexOf('\'', firstQuoteIndex + 1); + + // 检查两个引号是否都存在且位置有效 + if (firstQuoteIndex != -1 && secondQuoteIndex != -1 && secondQuoteIndex > firstQuoteIndex) { + // 提取两个引号之间的子字符串 + return input.substring(firstQuoteIndex + 1, secondQuoteIndex); + } + + // 如果未找到有效的引号对,返回null + return null; + } + + + public static void main(String[] args) { + + int 毛收入=1890 +1381 +60+100+80+380 -50-30;//支付宝加微信加dps端收入 + int 净收入 = 毛收入 -245-20;//扣除服务器费用和会员费 + int 董收入= (int) (净收入*0.7); + int 扣除dps端已经付给董的费用 = 董收入 -(60+100+80+380); + int 最终结果 = 扣除dps端已经付给董的费用 - (1220 +100+ 3600 +95 +50); //1220为上月遗留 3600为贪玩账单 剩下的吃饭 + + + + System.out.println(最终结果); + + + + } +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/UserServerProjectService.java b/旧的java项目/src/main/java/com/example/sjkbf/service/UserServerProjectService.java new file mode 100644 index 0000000..4c547c1 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/service/UserServerProjectService.java @@ -0,0 +1,57 @@ +package com.example.sjkbf.service; + +import com.example.sjkbf.entity.UserServerProject; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + *

+ * 用户服务器插件 服务类 + *

+ * + * @author bozhiqiang + * @since 2025-04-19 + */ +public interface UserServerProjectService extends IService { + + + /** + * 给服务器添加或延长插件时间 + * @param ip 服务器ip + * @param projectName 插件名字 + * @param time 时间 + * @param userName 用户名 + */ + void addpro(String ip,String projectName,int time,String userName); + + /** + * 查询此服务器下的插件列表 + */ + List getpro(String ip); + + + /** + * 查询这个服务器下开启的插件列表 + * @param ip + * @return + */ + List getproAndOpen(String ip); + + /** + * 查询这个服务器下的插件 + * @param serverIp + * @param projectName + * @return + */ + UserServerProject getByIPAndName(String serverIp, String projectName); + + /** + * 查询这个账号下的所有插件 + */ + List getByUserName(String userName); + + String setproOpen(String ip, String projectName, String open, String userName); + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/UserService.java b/旧的java项目/src/main/java/com/example/sjkbf/service/UserService.java new file mode 100644 index 0000000..bcb90cf --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/service/UserService.java @@ -0,0 +1,33 @@ +package com.example.sjkbf.service; + +import com.example.sjkbf.entity.DTO.LoginRequest; +import com.example.sjkbf.entity.User; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 用户 服务类 + *

+ * + * @author bozhiqiang + * @since 2025-04-15 + */ +public interface UserService extends IService { + public boolean isUsernameExist(String username); + + public void registerUser(LoginRequest request); + + String buy(String ip, String projectName, String userName); + + void updatePassword(LoginRequest request); + + String addServer(String ip, String userName, String account, String password, String port); + + String deleteServer(String ip, String userName); + + String setKey(String userName, String clientkey, String serverkey); + + String addgold(String userName, Integer gold); + + String transfer(String myName, String username, Integer gold); +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/impl/LogServiceImpl.java b/旧的java项目/src/main/java/com/example/sjkbf/service/impl/LogServiceImpl.java new file mode 100644 index 0000000..9894caf --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/service/impl/LogServiceImpl.java @@ -0,0 +1,21 @@ +package com.example.sjkbf.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.example.sjkbf.mapper.LogMapper; +import com.example.sjkbf.service.LogService; +import com.example.sjkbf.entity.Log; +import org.springframework.stereotype.Service; + +/** + *

+ * 服务实现类 + *

+ * + * @author bozhiqiang + * @since 2025-09-25 + */ +@Service +public class LogServiceImpl extends ServiceImpl implements LogService { + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/impl/ProjectPerServiceImpl.java b/旧的java项目/src/main/java/com/example/sjkbf/service/impl/ProjectPerServiceImpl.java new file mode 100644 index 0000000..da191a1 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/service/impl/ProjectPerServiceImpl.java @@ -0,0 +1,388 @@ +package com.example.sjkbf.service.impl; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.file.FileReader; +import cn.hutool.core.lang.TypeReference; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.symmetric.SymmetricAlgorithm; +import cn.hutool.crypto.symmetric.SymmetricCrypto; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.example.sjkbf.common.FileManage; +import com.example.sjkbf.entity.ProjectPer; +import com.example.sjkbf.entity.projectconfig.Project; +import com.example.sjkbf.entity.projectconfig.ProjectConfig; +import com.example.sjkbf.mapper.ProjectPerMapper; +import com.example.sjkbf.service.ProjectPerService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.example.sjkbf.util.RSATooL; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

+ * 服务实现类 + *

+ * + * @author bozhiqiang + * @since 2025-04-15 + */ +@Service +public class ProjectPerServiceImpl extends ServiceImpl implements ProjectPerService { + + + @Autowired + FileManage fileManage; + +// public ProjectConfig projectConfig; + + + //key为文件夹名字 y为json + Map projectScriptMap; + + public String configMd5=""; + + @Override + public Project getByName(String projectName) { + + if (projectName != null){ + return projectScriptMap.get("max").getProjectScript().get(projectName); + } + return null; + } + + + + @Override + public Map> getProjectInfoList() { + + Map projectScript = projectScriptMap.get("max").getProjectScript(); + Set strings = projectScript.keySet(); + + Map> info = new HashMap<>(); + for (String string : strings) { + //如果是私有的就不放进去 + if(projectScript.get(string).getPrivate())continue; + + Map info1 = projectScript.get(string).getInfo(); + info1.put("ProjectPrice", projectScript.get(string).getPrice()); + info.put(string, info1); + + } + return info; + } + + @Override + public Map> getProjectInfoListAll() { + + Map projectScript = projectScriptMap.get("max").getProjectScript(); + Set strings = projectScript.keySet(); + + Map> info = new HashMap<>(); + for (String string : strings) { + Map info1 = projectScript.get(string).getInfo(); + info1.put("ProjectPrice", projectScript.get(string).getPrice()); + info.put(string, info1); + } + return info; + } + + /** + * 获得所有的base脚本 + * 需要一个版本号 + * @return + */ + @Override + public List getBaseScriptStr(String ve) { + return projectScriptMap.get(ve).getBaseScriptStr(); + } + + public List getCSBaseScriptStr(String ve) { + return projectScriptMap.get(ve).getCSBaseScriptStr(); + } + + /** + * 根据key对base脚本进行加密 + * @param key + * @return + */ + @Override + public List getBaseScriptStrEncrypt(int[] key,String ve) { + List baseScriptStr = getBaseScriptStr(ve); + List StrEncrypt = new ArrayList<>(); + + for (String s : baseScriptStr) { + byte[] makecode = RSATooL.Makecode(s.getBytes(), key); + + String string = Arrays.toString(makecode); + StrEncrypt.add(string.substring(1, string.length()-1)); + + } + return StrEncrypt; + } + + @Override + public List getCSBaseScriptStrEncrypt(int[] key,String ve) { + List baseScriptStr = getCSBaseScriptStr(ve); + List StrEncrypt = new ArrayList<>(); + + for (String s : baseScriptStr) { + byte[] makecode = RSATooL.Makecode(s.getBytes(), key); + + String string = Arrays.toString(makecode); + StrEncrypt.add(string.substring(1, string.length()-1)); + + } + return StrEncrypt; + } + + /** + * 根据key对base脚本进行加密 + * + * @param key + * @return + */ + public List getBaseScriptStrEncrypt2(int[] key,String ve) { + List baseScriptStr = getBaseScriptStr(ve); + List StrEncrypt = new ArrayList<>(); + + for (String s : baseScriptStr) { + + byte[] makecode = RSATooL.Makecode(s.getBytes(), key); + + String string = Arrays.toString(makecode); + StrEncrypt.add(makecode); + } + return StrEncrypt; + } + + @Override + public List getzdy(String ip, int[] key,String ve) { + + Map> projectScriptStr = projectScriptMap.get(ve).getProjectZDYScriptStr(); + if (projectScriptStr==null)return new ArrayList<>(); + List strings = projectScriptStr.get(ip); + if (strings==null)return new ArrayList<>(); + + List StrEncrypt = new ArrayList<>(); + for (String s : strings) { + byte[] makecode = RSATooL.Makecode(s.getBytes(), key); + String string = Arrays.toString(makecode); + StrEncrypt.add(string.substring(1, string.length()-1)); + } + return StrEncrypt; + } + + + byte[] AESkey = new byte[]{120, -2, 20, -80, -24, 60, 62, -48, -18, 101, -117, -8, 52, 48, -59, 94}; + SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, AESkey); + + @Override + public List getzdy2(String ip,String ve) { + ve = "max"; + Map> projectScriptStr = projectScriptMap.get(ve).getProjectZDYScriptStr(); + if (projectScriptStr==null)return new ArrayList<>(); + List strings = projectScriptStr.get(ip); + if (strings==null)return new ArrayList<>(); + + List StrEncrypt = new ArrayList<>(); + for (String s : strings) { + + //加密为16进制表示 + String encryptHex = aes.encryptHex(s); + StrEncrypt.add(encryptHex); + } + return StrEncrypt; + } + + + + @Override + public List getProjectScriptStr(String projectName,String ve) { + List strings = projectScriptMap.get(ve).getProjectScriptStr().get(projectName); + return strings; + } + + + + @Override + public List getProjectScriptStrEncrypt(String projectName,int[] key,String ve) { + Map> projectScriptStr = projectScriptMap.get(ve).getProjectScriptStr(); + if (projectScriptStr==null)return new ArrayList<>(); + List strings = projectScriptStr.get(projectName); + List StrEncrypt = new ArrayList<>(); + for (String s : strings) { + byte[] makecode = RSATooL.Makecode(s.getBytes(), key); + String string = Arrays.toString(makecode); + StrEncrypt.add(string.substring(1, string.length()-1)); + } + return StrEncrypt; + } + + @Override + public List getProjectScriptStrEncrypt2(String projectName,int[] key,String ve) { + Map> projectScriptStr = projectScriptMap.get(ve).getProjectScriptStr(); + if (projectScriptStr==null)return new ArrayList<>(); + List strings = projectScriptStr.get(projectName); + List StrEncrypt = new ArrayList<>(); + for (String s : strings) { + byte[] makecode = RSATooL.Makecode(s.getBytes(), key); + String string = Arrays.toString(makecode); + StrEncrypt.add(makecode); + } + return StrEncrypt; + } + + + @Override + public int getProjectPrice(String projectName) { + if (projectScriptMap.get("max").getProjectScript().get(projectName)!=null){ + return projectScriptMap.get("max").getProjectScript().get(projectName).getPrice(); + } + return 0; + } + + + public synchronized void initInfo(){ + projectScriptMap = new HashMap<>(); + System.out.println("初始化项目信息"); + + String scriptfile = fileManage.getScriptfile(); + //获得这个文件夹下的所有子文件夹 + File[] files = FileUtil.ls(scriptfile); + + File[] subDirs = Arrays.stream(files) + .filter(File::isDirectory) // 仅保留子文件夹 + .toArray(File[]::new); + + // 2. 使用自定义自然排序比较器排序 + Arrays.sort(subDirs, new NaturalNameComparator()); + + for (File file : subDirs) { + //取Script 之后的文件名 + String name = file.getName().substring(7); + System.out.println( name); + ProjectConfig projectConfig = initInfo(file.getPath()); + projectScriptMap.put(name, projectConfig); + projectScriptMap.put("max", projectConfig); + } + + } + + public static void main(String[] args) { + String content = "test中文"; + + + + + + } + + //读取文件 + public synchronized ProjectConfig initInfo( String path) { + FileReader fileReader = new FileReader(path+"/"+"FileConfig.json"); + String result = fileReader.readString(); + ProjectConfig projectConfig = JSONUtil.toBean(result, new TypeReference() {}, true); + + List baseScript = projectConfig.getBaseScript(); + + + for (String s : baseScript) { + FileReader fileReader1 = new FileReader(path+"/"+s); + projectConfig.getBaseScriptStr().add(fileReader1.readString()); + } + +/* + List csBaseScript = projectConfig.getCSBaseScript(); + for (String s : csBaseScript) { + FileReader fileReader1 = new FileReader(path+"/"+s); + projectConfig.getCSBaseScriptStr().add(fileReader1.readString()); + }*/ + + + + Set strings = projectConfig.getProjectScript().keySet(); + + for (String string : strings) { + + List script = projectConfig.getProjectScript().get(string).getScript(); + projectConfig.getProjectScriptStr().put(string,new ArrayList<>()); + for (String s : script) { + FileReader fileReader1 = new FileReader(path+"\\"+s); + projectConfig.getProjectScriptStr().get(string).add(fileReader1.readString()); + } + } + + File[] ls = FileUtil.ls("/root/rindro/ClentUserScript"); + + for (File l : ls) { + Path path1 = Paths.get(l.getPath()); + List ZDYScript = new ArrayList<>(); + if (Files.exists(path1)) { + List strings444 = FileUtil.readUtf8Lines(l + "/config"); + for (String s : strings444) { + File file2 = FileUtil.file(l + "/" + s); + FileReader fileReader1 = new FileReader(file2); + ZDYScript.add(fileReader1.readString()); + } + } + projectConfig.getProjectZDYScriptStr().put(l.getName(),ZDYScript); + } + + + return projectConfig; + } + + + + private static final Pattern NUMBER_PATTERN = Pattern.compile("(\\d+\\.?\\d*)|([^\\d]+)"); + + + // 自定义自然排序比较器(核心) + static class NaturalNameComparator implements Comparator { + @Override + public int compare(File f1, File f2) { + String name1 = f1.getName(); + String name2 = f2.getName(); + + // 拆分两个名称的数字/非数字片段,逐个比较 + Matcher m1 = NUMBER_PATTERN.matcher(name1); + Matcher m2 = NUMBER_PATTERN.matcher(name2); + + while (m1.find() && m2.find()) { + String segment1 = m1.group(); + String segment2 = m2.group(); + + int compareResult; + // 判断当前片段是否是数字 + if (segment1.matches("\\d+\\.?\\d*") && segment2.matches("\\d+\\.?\\d*")) { + // 数字片段:按数值大小比较(支持小数) + double num1 = Double.parseDouble(segment1); + double num2 = Double.parseDouble(segment2); + compareResult = Double.compare(num1, num2); + } else { + // 非数字片段:按字符串字典序比较(忽略大小写,可选) + compareResult = segment1.compareToIgnoreCase(segment2); + } + + // 只要有一个片段比较出结果,就返回 + if (compareResult != 0) { + return compareResult; + } + } + + // 前面的片段都相同,短名称排前面 + return Integer.compare(name1.length(), name2.length()); + } + } +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserAndServerServiceImpl.java b/旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserAndServerServiceImpl.java new file mode 100644 index 0000000..7e45f46 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserAndServerServiceImpl.java @@ -0,0 +1,58 @@ +package com.example.sjkbf.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.example.sjkbf.entity.UserAndServer; +import com.example.sjkbf.mapper.UserAndServerMapper; +import com.example.sjkbf.service.UserAndServerService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + *

+ * 服务实现类 + *

+ * + * @author bozhiqiang + * @since 2025-04-17 + */ +@Service +public class UserAndServerServiceImpl extends ServiceImpl implements UserAndServerService { + + @Autowired + private UserAndServerMapper userAndServerMapper; + + + @Override + public int selectCount(String ip, String userName) { + + return userAndServerMapper.selectCount(new QueryWrapper().eq("server_ip",ip).eq("user_name",userName)); + } + + @Override + public List getServerList(String userName) { + + List userName1 = list(new QueryWrapper().eq("user_name", userName)); + + return userName1; + } + + @Override + public String updateServer(String ip, String userName, String account, String password, String port) { + UserAndServer one = getOne(new QueryWrapper().eq("server_ip", ip).eq("user_name", userName)); + if (one != null){ + one.setAccount(account); + one.setPassword(password); + one.setPort(port); + return updateById(one)?"修改成功":"修改失败"; + } + + return "服务器不存在"; + } + + + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserDetailsServiceImpl.java b/旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserDetailsServiceImpl.java new file mode 100644 index 0000000..ca57428 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserDetailsServiceImpl.java @@ -0,0 +1,51 @@ +package com.example.sjkbf.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.example.sjkbf.entity.AuthUser; +import com.example.sjkbf.entity.User; +import com.example.sjkbf.mapper.UserMapper; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +/** + *用户认证类 + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + + private final UserMapper userMapper; + + // 构造器注入(推荐) + public UserDetailsServiceImpl(UserMapper userMapper) { + this.userMapper = userMapper; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + User user = null; + try { + + // 1. 查询数据库 + user= userMapper.selectOne(new QueryWrapper().eq("username", username)); + }catch (Exception e){ + e.printStackTrace(); + } + + // 2. 用户不存在 + if (user == null) { + throw new UsernameNotFoundException("用户不存在"); + } + + // 3. 转换为 Spring Security 的 UserDetails 对象 + return new AuthUser( + user.getId(), + user.getUsername(), + user.getPassword(), + AuthorityUtils.createAuthorityList("ROLE_ALL") // 假设 roles 字段存储如 "ROLE_ADMIN,ROLE_USER" + ); + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserServerProjectServiceImpl.java b/旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserServerProjectServiceImpl.java new file mode 100644 index 0000000..c1d8800 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserServerProjectServiceImpl.java @@ -0,0 +1,93 @@ +package com.example.sjkbf.service.impl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.example.sjkbf.entity.UserServerProject; +import com.example.sjkbf.mapper.UserServerProjectMapper; +import com.example.sjkbf.service.UserServerProjectService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; + +/** + *

+ * 服务实现类 + *

+ * + * @author bozhiqiang + * @since 2025-04-19 + */ +@Service +public class UserServerProjectServiceImpl extends ServiceImpl implements UserServerProjectService { + + + + @Override + public void addpro(String ip, String name, int time, String userName) { + UserServerProject one = getOne(new QueryWrapper().eq("server_ip", ip).eq("project_name", name)); + if (one==null){ + UserServerProject userServerProject = new UserServerProject(); + userServerProject.setServerIp(ip); + userServerProject.setProjectName(name); + userServerProject.setUserName(userName); + userServerProject.setOpen(1); + DateTime date = DateUtil.date(); + DateTime newDate2 = DateUtil.offsetDay(date, time); + userServerProject.setExpirationDate(newDate2); + save(userServerProject); + }else { + Date expirationDate = one.getExpirationDate(); + DateTime newDate2 = DateUtil.offsetDay(expirationDate, time); + one.setExpirationDate(newDate2); + update(one, new QueryWrapper().eq("server_ip", ip).eq("project_name", name)); + } + } + + @Override + public List getpro(String ip) { + + List list = list(new QueryWrapper().eq("server_ip", ip).ge("expiration_date", DateUtil.date())); + + + return list; + } + + @Override + public List getproAndOpen(String ip) { + + List list = list(new QueryWrapper().eq("server_ip", ip).eq("open", 1).ge("expiration_date", DateUtil.date())); + + + return list; + } + + @Override + public UserServerProject getByIPAndName(String serverIp, String projectName) { + + return getOne(new QueryWrapper().eq("server_ip", serverIp).eq("project_name", projectName).ge("expiration_date", DateUtil.date())); + + } + + @Override + public List getByUserName(String userName) { + + return list(new QueryWrapper().eq("user_name", userName).ge("expiration_date", DateUtil.date())); + } + + @Override + public String setproOpen(String ip, String projectName, String open, String userName) { + UserServerProject one = getOne(new QueryWrapper().eq("server_ip", ip).eq("project_name", projectName)); + if (one!=null){ + one.setOpen(Integer.parseInt(open)); + update(one, new QueryWrapper().eq("server_ip", ip).eq("project_name", projectName)); + return "操作成功"; + }else { + return "账号下不存在此项目,请检查项目所有权是否为该账号"; + } + } + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserServiceImpl.java b/旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..dd4a0b0 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/service/impl/UserServiceImpl.java @@ -0,0 +1,292 @@ +package com.example.sjkbf.service.impl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.file.FileWriter; +import cn.hutool.log.StaticLog; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.example.sjkbf.common.FileManage; +import com.example.sjkbf.controller.verifyController; +import com.example.sjkbf.entity.DTO.LoginRequest; +import com.example.sjkbf.entity.Log; +import com.example.sjkbf.entity.ProjectPer; +import com.example.sjkbf.entity.User; +import com.example.sjkbf.entity.UserAndServer; +import com.example.sjkbf.entity.projectconfig.Project; +import com.example.sjkbf.mapper.UserMapper; +import com.example.sjkbf.service.*; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.Objects; +import java.util.Random; + +/** + *

+ * 服务实现类 + *

+ * + * @author bozhiqiang + * @since 2025-04-15 + */ +@Service +public class UserServiceImpl extends ServiceImpl implements UserService { + + @Autowired + private UserMapper userMapper; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private ProjectPerService projectPerService; + + + @Autowired + private UserAndServerService userAndServerService; + + @Autowired + private UserServerProjectService userServerProjectService; + + + @Autowired + private LogService logService; + + @Autowired + FileManage fileManage; + // 检查用户名是否存在 + public boolean isUsernameExist(String username) { + return userMapper.selectCount(new QueryWrapper().eq("username", username)) > 0; + } + + + // 注册用户 + public void registerUser(LoginRequest request) { + User user = new User(); + user.setUsername(request.getUsername()); + user.setPassword(passwordEncoder.encode(request.getPassword())); // 密码加密 + user.setQq(request.getQq()); + user.setGold(0); + user.setName(generateRandomString()); + save(user); + } + public static String generateRandomString() { + String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 8; i++) { + int index = random.nextInt(characters.length()); + sb.append(characters.charAt(index)); + } + return sb.toString(); + } + + + + + @Override + @Transactional//开启事务 保证同时成功或者失败 + public String buy(String ip, String projectName, String userName) { + //获得插件项目信息 + Project byName = projectPerService.getByName(projectName); + if (byName==null)return "查询不到该插件项目"; + if (userAndServerService.selectCount(ip, userName)==0){ + return "账号下不存在此服务器"; + } + + + User username = getOne(new QueryWrapper().eq("username", userName)); + + int demandGold = projectPerService.getProjectPrice(projectName); + if (demandGold>0) { + StaticLog.info("用户:"+userName+"购买项目:"+projectName+" IP:"+userName); + //扣减金币 + if (username.getGold() < demandGold){ + StaticLog.info("用户:"+userName+"购买项目:"+projectName+" IP:"+userName+"积分不足"); + return "积分不足"; + } + username.setGold(username.getGold() - demandGold); + updateById(username); + + verifyController.ipAndMac.remove( ip); + } + // 这里还要对服务器脚本数据库进行添加 + userServerProjectService.addpro(ip, projectName, 31, userName); + + try { + FileWriter writer = new FileWriter("/root/java/"+"log.txt"); + String now = DateUtil.now(); + writer.append(now + "用户:"+userName+"购买项目:"+projectName+" IP:"+userName +"购买完成"); + + Log log1 = new Log(); + log1.setName(userName); + log1.setLog("购买项目:"+projectName+" IP:"+ip +"购买完成,花费积分"+ demandGold); + + if (demandGold>0){ + log1.setType(3); + }else { + log1.setType(2); + } + + log1.setTime(DateUtil.date()); + logService.save(log1); + }catch (Exception e){ + StaticLog.info("用户:"+userName+"购买项目:"+projectName+" IP:"+userName+"写入日志失败"); + e.printStackTrace(); + } + + + + return "购买成功"; + } + + @Override + public void updatePassword(LoginRequest request) { + String username = request.getUsername(); + User username1 = getOne(new QueryWrapper().eq("username", username)); + + if (username1!=null && Objects.equals(request.getQq(), username1.getQq())){ + username1.setPassword(passwordEncoder.encode(request.getPassword())); + updateById(username1); + }else { + throw new RuntimeException("验证信息错误"); + } + } + + @Override + public String addServer(String ip, String userName, String account, String password, String port) { +// if (userAndServerService.selectCount(ip, userName)>0){ +// return "该服务器已存在,请不要重复添加"; +// } + + UserAndServer one = userAndServerService.getOne(new QueryWrapper().eq("server_ip", ip).eq("user_name", userName)); + if (one!=null){ + one.setAccount(account); + one.setPassword(password); + one.setPort(port); + + userAndServerService.update(one, new QueryWrapper().eq("server_ip", ip).eq("user_name", userName)); + + }else { + UserAndServer userAndServer = new UserAndServer(); + userAndServer.setUserName(userName); + userAndServer.setServerIp(ip); + userAndServer.setAccount(account); + userAndServer.setPassword(password); + userAndServer.setPort(port); + userAndServerService.save(userAndServer); + } + + return "添加成功"; + } + + @Override + public String deleteServer(String ip, String userName) { + if (userAndServerService.selectCount(ip, userName)>0){ + userAndServerService.remove(new QueryWrapper().eq("server_ip", ip).eq("user_name", userName)); + return "删除成功"; + } + return "删除失败"; + } + + @Override + public String setKey(String userName, String clientkey, String serverkey) { + User username = getOne(new QueryWrapper().eq("username", userName)); + if (username!=null){ + if (clientkey!=null) { + username.setClientKey(clientkey); + } + if (serverkey!=null) { + username.setServerKey(serverkey); + } + updateById(username); + return "设置成功"; + } + + + return "设置 请重新登录后再试"; + } + + @Override + public String addgold(String userName, Integer gold) { + + User username = getOne(new QueryWrapper().eq("username", userName)); + if (gold == 200){ + gold= 220; + } + if (gold == 500){ + gold= 570; + } + + if (username!=null){ + username.setGold(username.getGold()+gold); + updateById(username); + //记录日志 + FileWriter writer = new FileWriter(fileManage.getFile()+"log.txt"); + String now = DateUtil.now(); + writer.append(now+ ":"+userName+"充值"+gold+"成功\r\n"); + Log log1 = new Log(); + log1.setName(userName); + log1.setLog("充值"+gold+"成功"); + log1.setType(1); + log1.setTime(DateUtil.date()); + logService.save(log1); + + + return "充值成功"; + } + return "充值失败"; + } + + @Override + @Transactional + public String transfer(String myName, String username, Integer gold) { + // 按固定顺序锁定账户,避免死锁 + String firstLock = myName.compareTo(username) < 0 ? myName : username; + String secondLock = myName.compareTo(username) < 0 ? username : myName; + + // 使用for update锁定账户 + User firstUser = getOne(new QueryWrapper() + .eq("username", firstLock) + .last("for update")); + + User secondUser = getOne(new QueryWrapper() + .eq("username", secondLock) + .last("for update")); + + // 由于锁定顺序固定,我们需要根据实际转账方向确定哪个是转出账户,哪个是转入账户 + User myUser = firstLock.equals(myName) ? firstUser : secondUser; + User targetUser = firstLock.equals(myName) ? secondUser : firstUser; + + if (targetUser == null) { + return "被转让用户不存在,请检查用户名"; + } + + if (myUser.getGold() < gold) { + return "余额不足"; + } + + // 更新余额 + myUser.setGold(myUser.getGold() - gold); + updateById(myUser); + + targetUser.setGold(targetUser.getGold() + gold); + updateById(targetUser); + + // 记录日志 + Log log1 = new Log(); + log1.setName(myName); + log1.setLog(myName + "向" + username + "转让" + gold + "积分"); + log1.setType(4); + log1.setTime(DateUtil.date()); + logService.save(log1); + + return "转让完毕"; + } + + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/util/JavaFXVideoPlayer.java b/旧的java项目/src/main/java/com/example/sjkbf/util/JavaFXVideoPlayer.java new file mode 100644 index 0000000..d95278b --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/util/JavaFXVideoPlayer.java @@ -0,0 +1,340 @@ +package com.example.sjkbf.util; + +import javafx.application.Application; +import javafx.application.Platform; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.layout.*; +import javafx.scene.media.Media; +import javafx.scene.media.MediaPlayer; +import javafx.scene.media.MediaView; +import javafx.stage.Stage; +import javafx.util.Duration; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * JavaFX视频播放器 - 用于测试服务端视频流 + */ +public class JavaFXVideoPlayer extends Application { + + private MediaPlayer mediaPlayer; + private MediaView mediaView; + private Label statusLabel; + private ProgressBar progressBar; + private TextField projectField; + private TextField videoNameField; + private TextField serverUrlField; + private Button playButton; + private Button pauseButton; + private Button stopButton; + private Slider volumeSlider; + private Slider seekSlider; + private DoubleProperty playbackRate; + + private static final String DEFAULT_SERVER = "http://localhost:8651"; + + @Override + public void start(Stage primaryStage) { + playbackRate = new SimpleDoubleProperty(1.0); + + // 创建UI组件 + createUIComponents(); + + // 布局 + VBox root = createLayout(); + + // 场景 + Scene scene = new Scene(root, 1000, 700); + scene.getStylesheets().add(getClass().getResource("/styles.css").toExternalForm()); + + // 舞台设置 + primaryStage.setTitle("视频流测试播放器"); + primaryStage.setScene(scene); + primaryStage.setOnCloseRequest(event -> cleanup()); + primaryStage.show(); + } + + private void createUIComponents() { + // 输入字段 + serverUrlField = new TextField(DEFAULT_SERVER); + serverUrlField.setPromptText("服务器地址"); + + projectField = new TextField("战令"); + projectField.setPromptText("项目名称"); + + videoNameField = new TextField("2"); + videoNameField.setPromptText("视频名称"); + + // 控制按钮 + playButton = new Button("播放"); + playButton.setOnAction(e -> playVideo()); + + pauseButton = new Button("暂停"); + pauseButton.setOnAction(e -> pauseVideo()); + pauseButton.setDisable(true); + + stopButton = new Button("停止"); + stopButton.setOnAction(e -> stopVideo()); + stopButton.setDisable(true); + + // 媒体视图 + mediaView = new MediaView(); + mediaView.setFitWidth(800); + mediaView.setFitHeight(450); + mediaView.setPreserveRatio(true); + + // 进度条和滑块 + progressBar = new ProgressBar(0); + progressBar.setPrefWidth(800); + + seekSlider = new Slider(0, 100, 0); + seekSlider.setPrefWidth(800); + seekSlider.valueProperty().addListener((obs, oldVal, newVal) -> { + if (seekSlider.isValueChanging() && mediaPlayer != null) { + Duration duration = mediaPlayer.getTotalDuration(); + if (duration != null) { + mediaPlayer.seek(duration.multiply(newVal.doubleValue() / 100.0)); + } + } + }); + + // 音量控制 + volumeSlider = new Slider(0, 100, 50); + volumeSlider.setPrefWidth(150); + volumeSlider.valueProperty().addListener((obs, oldVal, newVal) -> { + if (mediaPlayer != null) { + mediaPlayer.setVolume(newVal.doubleValue() / 100.0); + } + }); + + // 状态标签 + statusLabel = new Label("准备就绪"); + statusLabel.setStyle("-fx-text-fill: #2E8B57; -fx-font-weight: bold;"); + } + + private VBox createLayout() { + VBox root = new VBox(15); + root.setPadding(new Insets(15)); + root.setStyle("-fx-background-color: #2F4F4F;"); + + // 标题 + Label titleLabel = new Label("服务端视频流测试播放器"); + titleLabel.setStyle("-fx-text-fill: white; -fx-font-size: 20px; -fx-font-weight: bold;"); + + // 输入区域 + GridPane inputGrid = new GridPane(); + inputGrid.setHgap(10); + inputGrid.setVgap(10); + inputGrid.setPadding(new Insets(10)); + inputGrid.setStyle("-fx-background-color: #556B2F; -fx-background-radius: 5;"); + + inputGrid.add(new Label("服务器:"), 0, 0); + inputGrid.add(serverUrlField, 1, 0); + inputGrid.add(new Label("项目:"), 0, 1); + inputGrid.add(projectField, 1, 1); + inputGrid.add(new Label("视频:"), 0, 2); + inputGrid.add(videoNameField, 1, 2); + + serverUrlField.setPrefWidth(300); + projectField.setPrefWidth(200); + videoNameField.setPrefWidth(200); + + // 控制按钮区域 + HBox controlBox = new HBox(10); + controlBox.setAlignment(Pos.CENTER); + controlBox.getChildren().addAll(playButton, pauseButton, stopButton); + + // 音量控制 + HBox volumeBox = new HBox(10); + volumeBox.setAlignment(Pos.CENTER_LEFT); + volumeBox.getChildren().addAll(new Label("音量:"), volumeSlider); + + // 播放速率控制 + HBox rateBox = new HBox(10); + rateBox.setAlignment(Pos.CENTER_LEFT); + ComboBox rateCombo = new ComboBox<>(); + rateCombo.getItems().addAll("0.5x", "1.0x", "1.5x", "2.0x"); + rateCombo.setValue("1.0x"); + rateCombo.setOnAction(e -> { + String selected = rateCombo.getValue(); + double rate = Double.parseDouble(selected.replace("x", "")); + playbackRate.set(rate); + if (mediaPlayer != null) { + mediaPlayer.setRate(rate); + } + }); + rateBox.getChildren().addAll(new Label("播放速度:"), rateCombo); + + // 状态区域 + HBox statusBox = new HBox(10); + statusBox.setAlignment(Pos.CENTER); + statusBox.getChildren().addAll(statusLabel, progressBar); + + // 组装所有组件 + root.getChildren().addAll( + titleLabel, + inputGrid, + controlBox, + new Separator(), + mediaView, + new Label("播放进度:"), + seekSlider, + new Separator(), + volumeBox, + rateBox, + statusBox + ); + + return root; + } + + private void playVideo() { + try { + String serverUrl = serverUrlField.getText().trim(); + String project = projectField.getText().trim(); + String videoName = videoNameField.getText().trim(); + + if (project.isEmpty() || videoName.isEmpty()) { + showError("请填写项目名称和视频名称"); + return; + } + + // 编码URL参数 + String encodedProject = URLEncoder.encode(project, StandardCharsets.UTF_8.toString()); + String encodedVideoName = URLEncoder.encode(videoName, StandardCharsets.UTF_8.toString()); + + String videoUrl = String.format("%s/api/videos/%s/%s", + serverUrl, encodedProject, encodedVideoName); + + updateStatus("正在连接: " + videoUrl); + + // 清理之前的播放器 + if (mediaPlayer != null) { + mediaPlayer.dispose(); + } + + // 创建新的媒体播放器 + Media media = new Media(videoUrl); + mediaPlayer = new MediaPlayer(media); + mediaView.setMediaPlayer(mediaPlayer); + + // 设置播放速率 + mediaPlayer.setRate(playbackRate.get()); + mediaPlayer.setVolume(volumeSlider.getValue() / 100.0); + + // 设置事件监听器 + setupMediaListeners(); + + // 开始播放 + mediaPlayer.play(); + updateStatus("正在播放: " + project + "/" + videoName); + + // 更新按钮状态 + playButton.setDisable(true); + pauseButton.setDisable(false); + stopButton.setDisable(false); + + } catch (UnsupportedEncodingException e) { + showError("URL编码错误: " + e.getMessage()); + } catch (Exception e) { + showError("播放错误: " + e.getMessage()); + e.printStackTrace(); + } + } + + private void setupMediaListeners() { + mediaPlayer.setOnReady(() -> { + updateStatus("媒体已加载完成"); + progressBar.setProgress(0); + }); + + mediaPlayer.setOnPlaying(() -> { + updateStatus("播放中..."); + }); + + mediaPlayer.setOnPaused(() -> { + updateStatus("已暂停"); + }); + + mediaPlayer.setOnStopped(() -> { + updateStatus("已停止"); + resetControls(); + }); + + mediaPlayer.setOnEndOfMedia(() -> { + updateStatus("播放完成"); + resetControls(); + progressBar.setProgress(1.0); + }); + + mediaPlayer.setOnError(() -> { + String errorMessage = mediaPlayer.getError() != null ? + mediaPlayer.getError().getMessage() : "未知错误"; + showError("媒体错误: " + errorMessage); + resetControls(); + }); + + // 更新进度条 + mediaPlayer.currentTimeProperty().addListener((obs, oldTime, newTime) -> { + if (mediaPlayer.getTotalDuration().greaterThan(Duration.ZERO)) { + double progress = newTime.toMillis() / mediaPlayer.getTotalDuration().toMillis(); + progressBar.setProgress(progress); + seekSlider.setValue(progress * 100); + } + }); + } + + private void pauseVideo() { + if (mediaPlayer != null) { + mediaPlayer.pause(); + pauseButton.setDisable(true); + playButton.setDisable(false); + } + } + + private void stopVideo() { + if (mediaPlayer != null) { + mediaPlayer.stop(); + resetControls(); + } + } + + private void resetControls() { + playButton.setDisable(false); + pauseButton.setDisable(true); + stopButton.setDisable(true); + progressBar.setProgress(0); + seekSlider.setValue(0); + } + + private void updateStatus(String message) { + Platform.runLater(() -> { + statusLabel.setText(message); + statusLabel.setStyle("-fx-text-fill: #2E8B57; -fx-font-weight: bold;"); + }); + } + + private void showError(String message) { + Platform.runLater(() -> { + statusLabel.setText("错误: " + message); + statusLabel.setStyle("-fx-text-fill: #FF4500; -fx-font-weight: bold;"); + }); + } + + private void cleanup() { + if (mediaPlayer != null) { + mediaPlayer.dispose(); + } + } + + public static void main(String[] args) { + launch(args); + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/util/JwtTokenUtil.java b/旧的java项目/src/main/java/com/example/sjkbf/util/JwtTokenUtil.java new file mode 100644 index 0000000..e5b49ac --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/util/JwtTokenUtil.java @@ -0,0 +1,171 @@ +package com.example.sjkbf.util; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; +import javax.crypto.SecretKey; +import javax.servlet.http.HttpServletRequest; +import java.util.*; +import java.util.stream.Collectors; + +/** + * JWT 工具类 + * - 负责 Token 的生成、解析、验证 + * - 采用 HMAC-SHA 算法进行签名,保证安全性 + * - 适配 JJWT 0.11.x 版本 + */ +@Component +public class JwtTokenUtil { + + // 从配置文件中注入 JWT 密钥和过期时间 + @Value("${jwt.secret}") + private String secret; // 密钥(至少 32 位,推荐 64 位随机字符串) + + @Value("${jwt.expiration}") + private Long expiration; // Token 有效期(秒) + + /** + * 生成安全的 HMAC-SHA 密钥 + * - 避免直接使用原始字符串,提升安全性 + */ + private SecretKey generateSecretKey() { + return Keys.hmacShaKeyFor(secret.getBytes()); + } + + /** + * 生成 Token + * @param userDetails 用户信息(包含用户名、权限等) + * @return Token 字符串 + */ + public String generateToken(UserDetails userDetails) { + // 1. 定义 Token 的 Claims(载荷) + Map claims = new HashMap<>(); + claims.put("username", userDetails.getUsername()); + // 将权限列表转换为字符串(例如 "ROLE_ADMIN,ROLE_USER") + claims.put("authorities", userDetails.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.toList())); + + // 2. 生成 Token + return Jwts.builder() + .setClaims(claims) // 自定义载荷 + .setSubject(userDetails.getUsername()) // 主题(通常放用户名) + .setIssuedAt(new Date()) // 签发时间 + .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)) // 过期时间 + .signWith(generateSecretKey()) // 签名算法和密钥 + .compact(); // 生成字符串 + } + + /** + * 从 Token 中解析用户名 + * @param token JWT Token + * @return 用户名 + */ + public String getUsernameFromToken(String token) { + return parseToken(token).getSubject(); + } + + /** + * 从 Token 中解析权限列表 + * @param token JWT Token + * @return 权限列表(例如 ["ROLE_ADMIN", "ROLE_USER"]) + */ + public List getAuthoritiesFromToken(String token) { + Claims claims = parseToken(token); + return claims.get("authorities", List.class); + } + + /** + * 验证 Token 是否有效(基础验证) + * - 检查签名是否正确 + * - 检查是否过期 + * @param token JWT Token + * @return 是否有效 + */ + public boolean validateToken(String token) { + + Claims claims = parseToken(token); + // 检查是否过期(parseToken 内部已自动验证过期时间) + return !claims.getExpiration().before(new Date()); + + } + + /** + * 验证 Token 是否属于指定用户(业务层扩展验证) + * @param token JWT Token + * @param userDetails 用户信息 + * @return 是否匹配 + */ + public boolean validateTokenBelongsToUser(String token, UserDetails userDetails) { + String username = getUsernameFromToken(token); + return username.equals(userDetails.getUsername()); + } + + /** + * 解析 Token(内部方法) + * @param token JWT Token + * @return 解析后的 Claims 对象 + * @throws JwtException 解析失败时抛出异常(如签名错误、格式错误) + */ + public Claims parseToken(String token) { + try { + return Jwts.parserBuilder() + .setSigningKey(generateSecretKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } catch (JwtException e) { + throw new JwtException("Token 已过期"); + } + } + // ---------- 扩展方法:自定义 Claims ---------- + + /** + * 添加自定义 Claim(在生成 Token 前调用) + * 示例:添加用户ID + * Map claims = new HashMap<>(); + * claims.put("userId", 123); + * String token = jwtTokenUtil.generateToken(userDetails, claims); + */ + public String generateToken(UserDetails userDetails, Map customClaims) { + Map claims = new HashMap<>(); + claims.putAll(customClaims); // 合并自定义 Claims + claims.put("authorities", userDetails.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.toList())); + + return Jwts.builder() + .setClaims(claims) + .setSubject(userDetails.getUsername()) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)) + .signWith(generateSecretKey()) + .compact(); + } + + /** + * 获取自定义 Claim + * 示例:Long userId = jwtTokenUtil.getClaimFromToken(token, "userId", Long.class); + */ + public T getClaimFromToken(String token, String claimName, Class clazz) { + Claims claims = parseToken(token); + return claims.get(claimName, clazz); + } + + + /** + * 从请求中获取 Token + * @param request + * @return + */ + public String getTokenFromRequest(HttpServletRequest request) { + String header = request.getHeader("Authorization"); + if (header != null && header.startsWith("Bearer ")) { + return header.substring(7); + } + return null; + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/util/MD5.java b/旧的java项目/src/main/java/com/example/sjkbf/util/MD5.java new file mode 100644 index 0000000..64679ed --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/util/MD5.java @@ -0,0 +1,44 @@ +package com.example.sjkbf.util; + +import java.security.MessageDigest; + +public class MD5 { + // 全局数组 + private final static String[] strDigits = {"0", "1", "2", "3", "4", "5", + "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "j", "?", "~"}; + + + // 返回形式为数字跟字符串 + private static String byteToArrayString(byte bByte) { + int iRet = bByte; + if (iRet < 0) { + iRet += 256; + } + int iD1 = iRet / 16; + int iD2 = iRet % 16; + return strDigits[iD1] + strDigits[iD2]; + } + + // 转换字节数组为16进制字串 + private static String byteToString(byte[] bByte) { + StringBuffer sBuffer = new StringBuffer(); + for (int i = 0; i < bByte.length; i++) { + sBuffer.append(byteToArrayString(bByte[i])); + } + return sBuffer.toString(); + } + + public static String getMD5Code(String strObj) { + String resultString = null; + try { +// resultString = new String(strObj); + MessageDigest md = MessageDigest.getInstance("MD5"); + // md.digest() 该函数返回值为存放哈希值结果的byte数组 + resultString = byteToString(md.digest(strObj.getBytes("UTF-8"))); + } catch (Exception e) { + e.printStackTrace(); + } + return resultString; + } + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/util/RSATooL.java b/旧的java项目/src/main/java/com/example/sjkbf/util/RSATooL.java new file mode 100644 index 0000000..b0a1dc7 --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/util/RSATooL.java @@ -0,0 +1,223 @@ +package com.example.sjkbf.util; + +import cn.hutool.core.codec.Base64; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public class RSATooL { + + + private static String prkey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANp/KnuAZq609lnc\n" + + "jciVgt/xDLDEaKC+DSuSZc9o/fgLqweL/8Yu5iPAzoBQlO1t+jSkI2YuoFsKKK7O\n" + + "QFcSoz4Z5Lb7vOG7XJ4Re/6L2LJIU9tjJ3LznW5WKnuiqxs0ziIZilLkRE0P1di6\n" + + "FIZQUjmn2KQ2y/P7PuviJPat3y+vAgMBAAECgYEAmn64h0MvV/FVEA1Ho8E0HSzQ\n" + + "kF0Qrjg0D88gdhwDGFUHxfpUDMo0qKs4WLqh05CkDnzRGvt1H2p7gb8M94SSL5SX\n" + + "mCR2ZLiCQJy5tZpQNRUt9P67upLcDcKJ6YPujcuEdlo0pzKGGMZk8Uvh05S9lnwl\n" + + "yWJeEk4G7q5Aalz8vIECQQDwBVJH60Gd2St/GS4dCtXwHTO9HJZRaWEkaXPgVbXI\n" + + "vqv+jtD4HpzDCpgv/kUK0fnb4V/9/LM1SHOedVMfmh0fAkEA6QsChdHiU1lHfL1b\n" + + "Z3Uvxvi/Zk8i0ryzBaYudrzNmb7iJcssJPPty3KTjihgvw2W36GV+2GlQBEjOf2B\n" + + "2WgLcQJBAO66XDxsIbd+aWThBpycSm2one1aoagXyCcPO9HFbilcfHWUVwRybjkQ\n" + + "MI6LuOAqOPoaD//vd89nYJga2bJ09sECQFmXRPoDTVozqXr4JSqp75szyAliBQY1\n" + + "SzGxyI0XWodvzesvp6HxMQsU2ks9lKv+YnFI4qsIyAnQTNWfcwsMp9ECQDba0LJV\n" + + "xI0SIznsVtBb7T5Ir9ppLKx1QTNcytkjHxmQqWGy+57ojjQ6GV3z4fSwNGjIhsOT\n" + + "DFgz0gEcRr13bRk="; + private static String pubkey = + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDafyp7gGautPZZ3I3IlYLf8Qyw\n" + + "xGigvg0rkmXPaP34C6sHi//GLuYjwM6AUJTtbfo0pCNmLqBbCiiuzkBXEqM+GeS2\n" + + "+7zhu1yeEXv+i9iySFPbYydy851uVip7oqsbNM4iGYpS5ERND9XYuhSGUFI5p9ik\n" + + "Nsvz+z7r4iT2rd8vrwIDAQAB"; + static RSA rsa2 = new RSA(prkey, pubkey); + + /** + * 解密 + * @param str + * @return + */ + public static String decrypt(String str){ + + str= str.replaceAll(" ","+"); + byte[] decrypt2 = rsa2.decrypt(str, KeyType.PrivateKey); + String s = new String(decrypt2); + String[] split = s.split(""); + s = split[0]; + return s; + } + + /** + * 加密 + * @param str + * @return + */ + public static String encryption(String str){ + return Base64.encode(rsa2.encrypt(str, KeyType.PrivateKey)); + } + + + + /** + * 使用RSA公钥解密 + * @param encryptedData 加密后的数据(Base64编码) + * @return 解密后的原始字符串 + */ + public static String decryptByPublicKey( String encryptedData) { + + + // 解密步骤: + // 1. 将Base64编码的加密数据解码为字节数组 + // 2. 使用公钥解密(指定KeyType.PublicKey) + // 3. 将解密后的字节数组转换为字符串 + byte[] decryptedBytes = rsa2.decrypt(Base64.decode(encryptedData), KeyType.PublicKey); + + return new String(decryptedBytes, StandardCharsets.UTF_8); + } + + + public static void main(String[] args) { + System.out.println(decompress("H4sIAAAAAAAAAKtWyk6tVLJSUggKdgzJz/fRS81LLqosKMnMz9MAyhTrleQHlxRl5qVraCrVAgAXNYHCLQAAAA==")); + } + + //异或加密 + public static byte[] Makecode(byte[] pstr,int[] Key) { + byte[] Str = pstr; + + for (int i = 0; i < Str.length; i++) { + byte sbuf = Str[i]; + Str[i] = MakecodeChar(sbuf, Key[i % 20], Key[(i + 18) % 20]); + } + + return Str; + } + + //字符串异或 + public static String encryptDecrypt(String input, String key) { + StringBuilder output = new StringBuilder(input); + int keyLength = key.length(); + + for (int i = 0; i < input.length(); i++) { + // 对每个字符进行异或运算 + output.setCharAt(i, (char) (input.charAt(i) ^ key.charAt(i % keyLength))); + } + + return output.toString(); + } + + + //单个字符加密 + public static byte MakecodeChar(byte c, int key, int key2) { + return (byte) ((((c + key) ^ key2) ^ key) + 1); + } + + + /** + * 单个字符解密,对应C++的CutcodeChar + * @param c 待解密的字符(Java中用byte表示,对应C++的char) + * @param key 第一个密钥 + * @param key2 第二个密钥 + * @return 解密后的字符(byte) + */ + public static byte cutcodeChar(byte c, int key, int key2) { + // 对应C++的:(((c - 1) ^ key) ^ key2) - key + // 注意:Java中byte是8位有符号数,运算时会自动提升为int,最后强转为byte + int step1 = (c - 1) ^ key; + int step2 = step1 ^ key2; + int result = step2 - key; + return (byte) result; + } + + /** + * 字节数组解密,对应C++的Cutecode + * @param pstr 待解密的字节数组(对应C++的char*) + * @param pkey 密钥数组 + * @param len 待解密的长度(-1表示使用整个数组长度) + * @param keyLength 密钥长度(默认5,与C++默认参数一致) + * @return 解密后的字节数组(原数组会被修改,同时返回新数组方便链式调用) + */ + public static byte[] cutecode(byte[] pstr, int[] pkey, int len, int keyLength) { + // 处理默认长度:如果len为-1,则使用数组的实际长度 + if (len == -1) { + len = pstr.length; + } + // 遍历每个字节进行解密 + for (int i = 0; i < len; i++) { + // 计算密钥索引,与C++逻辑一致 + int keyIndex = i % keyLength; + int key2Index = (i + 18) % keyLength; + // 调用单个字符解密方法,修改原数组 + pstr[i] = cutcodeChar(pstr[i], pkey[keyIndex], pkey[key2Index]); + } + return pstr; + } + + // 重载方法:提供默认参数(对应C++的默认参数int keyLength = 5) + public static byte[] cutecode(byte[] pstr, int[] pkey, int len) { + return cutecode(pstr, pkey, len, 5); + } + + // 重载方法:最简化调用(len=-1,keyLength=5) + public static byte[] cutecode(byte[] pstr, int[] pkey) { + return cutecode(pstr, pkey, -1, 5); + } + /** + * 压缩字符串 + * @param input 原始字符串 + * @return Base64编码后的压缩字符串(便于存储/传输) + */ + public static String compress(String input) { + if (input == null || input.isEmpty()) { + return input; + } + + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + GZIPOutputStream gzipOS = new GZIPOutputStream(bos)) { + + gzipOS.write(input.getBytes(StandardCharsets.UTF_8)); + gzipOS.finish(); + + // 将压缩后的字节数组转为Base64字符串(避免二进制乱码) + byte[] compressedBytes = bos.toByteArray(); + return java.util.Base64.getEncoder().encodeToString(compressedBytes); + + } catch (IOException e) { + throw new RuntimeException("压缩失败", e); + } + } + + + /** + * 解压字符串 + * @param compressedStr Base64编码后的压缩字符串 + * @return 解压后的原始字符串 + */ + public static String decompress(String compressedStr) { + if (compressedStr == null || compressedStr.isEmpty()) { + return compressedStr; + } + + byte[] compressedBytes = java.util.Base64.getDecoder().decode(compressedStr); + System.out.println(Arrays.toString(compressedBytes)); + try (ByteArrayInputStream bis = new ByteArrayInputStream(compressedBytes); + GZIPInputStream gzipIS = new GZIPInputStream(bis); + ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + + byte[] buffer = new byte[1024]; + int len; + while ((len = gzipIS.read(buffer)) != -1) { + bos.write(buffer, 0, len); + } + + return new String(bos.toByteArray(), StandardCharsets.UTF_8); + + } catch (IOException e) { + throw new RuntimeException("解压失败", e); + } + } + +} diff --git a/旧的java项目/src/main/java/com/example/sjkbf/util/SimpleNutZipCompatible.java b/旧的java项目/src/main/java/com/example/sjkbf/util/SimpleNutZipCompatible.java new file mode 100644 index 0000000..c03cf6f --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/util/SimpleNutZipCompatible.java @@ -0,0 +1,250 @@ +package com.example.sjkbf.util; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.file.FileReader; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.symmetric.AES; +import cn.hutool.crypto.symmetric.SymmetricAlgorithm; +import cn.hutool.json.JSONObject; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; + +import java.io.*; +import java.nio.file.*; +import java.util.Arrays; +import java.util.zip.*; + +public class SimpleNutZipCompatible { + + // 字节级别的加密解密 + public static byte[] encryptDecryptBytes(byte[] data, String key) { + byte[] result = new byte[data.length]; + byte[] keyBytes = key.getBytes(); + + for (int i = 0; i < data.length; i++) { + result[i] = (byte) (data[i] ^ keyBytes[i % keyBytes.length]); + } + return result; + } + + public static void main(String[] args) { + String operation = "encrypt"; // 或 "decrypt" + String sourcePath = "C:\\Users\\12134\\Desktop\\fsdownload\\DPS_Script"; + String targetPath = "C:\\Users\\12134\\Desktop\\fsdownload\\DPS_Script\\sut.tar.gz"; + String key = "Rindro-Aurora"; + + if ("encrypt".equals(operation)) { + // 加密并压缩 + encryptAndZip(sourcePath, targetPath, key); + } else { + // 解压并解密 + decryptFromZip(sourcePath, targetPath, key); + } + } + + + + /** + * 检查文件是否在_Core子目录下 + */ + private static boolean isInRootOrCore(Path filePath, Path sourcePath) { + Path relativePath = sourcePath.relativize(filePath); + int nameCount = relativePath.getNameCount(); + + // _Core子目录下的文件(相对路径的第一个目录是_Core) + if (nameCount >= 2 && "_Core".equals(relativePath.getName(0).toString())) { + return true; + } + + return false; + } + + + public static void encryptAndZip(String folderPath, String zipPath, String key) { + + // 随机生成密钥 + byte[] keyBytes = {92, -2, -65, 10, 118, 103, -68, 72, -23, 124, -103, -39, 0, -128, 31, -107}; + + + System.out.println("密钥: " + Arrays.toString(keyBytes)); + + // 构建 + AES aes = SecureUtil.aes(keyBytes); + + + Path tempDir = null; + try { + // 创建临时目录 + tempDir = Files.createTempDirectory("nut_encrypt"); + Path sourcePath = Paths.get(folderPath); + + // 加密所有.nut文件到临时目录 + Path finalTempDir = tempDir; + Files.walk(sourcePath) + .filter(Files::isRegularFile) + .filter(path -> (path.toString().toLowerCase().endsWith(".nut"))) + .filter(path -> isInRootOrCore(path, sourcePath)) + .forEach(nutFile -> { + try { + byte[] content = Files.readAllBytes(nutFile); + byte[] encrypted = aes.encrypt(content); + + Path relative = sourcePath.relativize(nutFile); + Path tempFile = finalTempDir.resolve(relative); + + String newName = tempFile.getFileName().toString().replace(".nut", ".nut"); + Path sutFile = tempFile.getParent().resolve(newName); + + Files.createDirectories(sutFile.getParent()); + Files.write(sutFile, encrypted); + + System.out.println("加密: " + relative); + } catch (Exception e) { + System.err.println("错误: " + nutFile + " - " + e.getMessage()); + } + }); + + + FileReader fileReader = new FileReader(folderPath + "/FileConfig.json"); + String result5 = fileReader.readString(); + //转json + JSONObject jsonObject = new JSONObject(result5); + + jsonObject.remove("ProjectScript"); + + + String string = tempDir.toAbsolutePath()+"/FileConfig.json"; + FileUtil.writeString(jsonObject.toString(),string, "utf-8"); + + + + // 压缩临时目录 + zipFolderToTarGz(tempDir.toString(), zipPath); + System.out.println("压缩完成: " + zipPath); + + } catch (Exception e) { + System.err.println("加密压缩失败: " + e.getMessage()); + } finally { + if (tempDir != null) { + deleteDirectory(tempDir.toFile()); + } + } + } + + public static void decryptFromZip(String zipPath, String outputFolder, String key) { + try { + Path outputPath = Paths.get(outputFolder); + Files.createDirectories(outputPath); + + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipPath))) { + ZipEntry entry; + byte[] buffer = new byte[1024]; + + while ((entry = zis.getNextEntry()) != null) { + if (!entry.isDirectory() && entry.getName().toLowerCase().endsWith(".sut")) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int len; + while ((len = zis.read(buffer)) > 0) { + baos.write(buffer, 0, len); + } + + byte[] encrypted = baos.toByteArray(); + byte[] decrypted = encryptDecryptBytes(encrypted, key); + + Path outputFile = outputPath.resolve(entry.getName()); + String newName = outputFile.getFileName().toString().replace(".sut", ".nut"); + Path nutFile = outputFile.getParent().resolve(newName); + + Files.createDirectories(nutFile.getParent()); + Files.write(nutFile, decrypted); + + System.out.println("解密: " + entry.getName()); + } + zis.closeEntry(); + } + } + + System.out.println("解密完成: " + outputFolder); + + } catch (Exception e) { + System.err.println("解压解密失败: " + e.getMessage()); + } + } + + private static void zipFolder(String srcFolder, String zipPath) { + try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath))) { + File folder = new File(srcFolder); + zipDirectory(folder, zos, ""); + } catch (IOException e) { + System.err.println("压缩错误: " + e.getMessage()); + } + } + + private static void zipDirectory(File folder, ZipOutputStream zos, String parentPath) throws IOException { + for (File file : folder.listFiles()) { + if (file.isDirectory()) { + zipDirectory(file, zos, parentPath + file.getName() + "/"); + } else { + try (FileInputStream fis = new FileInputStream(file)) { + zos.putNextEntry(new ZipEntry(parentPath + file.getName())); + byte[] buffer = new byte[1024]; + int length; + while ((length = fis.read(buffer)) > 0) { + zos.write(buffer, 0, length); + } + zos.closeEntry(); + } + } + } + } + + + /** + * 将文件夹压缩为 tar.gz 格式 + */ + private static void zipFolderToTarGz(String sourceFolder, String tarGzPath) throws IOException { + Path sourcePath = Paths.get(sourceFolder); + + try (FileOutputStream fos = new FileOutputStream(tarGzPath); + BufferedOutputStream bos = new BufferedOutputStream(fos); + GZIPOutputStream gos = new GZIPOutputStream(bos); + TarArchiveOutputStream tos = new TarArchiveOutputStream(gos)) { + + // 设置tar格式 + tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + + Files.walk(sourcePath) + .filter(Files::isRegularFile) + .forEach(file -> { + try { + // 获取相对路径 + Path relativePath = sourcePath.relativize(file); + + // 创建tar条目 + TarArchiveEntry entry = new TarArchiveEntry(file.toFile(), relativePath.toString().replace("\\", "/")); + tos.putArchiveEntry(entry); + + // 写入文件内容 + Files.copy(file, tos); + + tos.closeArchiveEntry(); +// System.out.println("添加到tar.gz: " + relativePath); + + } catch (IOException e) { + System.err.println("添加文件到tar.gz失败: " + file + " - " + e.getMessage()); + } + }); + + tos.finish(); + } + } + + private static void deleteDirectory(File dir) { + if (dir.isDirectory()) { + for (File file : dir.listFiles()) { + deleteDirectory(file); + } + } + dir.delete(); + } +} \ No newline at end of file diff --git a/旧的java项目/src/main/java/com/example/sjkbf/util/tool.java b/旧的java项目/src/main/java/com/example/sjkbf/util/tool.java new file mode 100644 index 0000000..d334dfe --- /dev/null +++ b/旧的java项目/src/main/java/com/example/sjkbf/util/tool.java @@ -0,0 +1,118 @@ +package com.example.sjkbf.util; + +import cn.hutool.core.codec.Base64; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; + +import javax.servlet.http.HttpServletRequest; + +public class tool { + + + public static String encryptDecrypt(String input, String key) { + char[] output = new char[input.length()]; + for (int i = 0; i < input.length(); i++) { + output[i] = (char) (input.charAt(i) ^ key.charAt(i % key.length())); // 使用异或运算进行加密 + } + return new String(output); + } + + + private static final String prkey = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMiYuNW4K1rST7ZW" + + "YpWX6nEziXi5JveLPhDCLj0VZ/5/4dzBWrmoL/IcFZuHOBJtYHm965713kKC9gtw" + + "2EyVgkqmXLT3105jEUqzNizfThc6C2ZL6vMmzUZlooxNyaOC5mWthPZtwlqQihYW" + + "T2nW/wKp8fpTouXihQOCPjqdRoVFAgMBAAECgYEAg+1dcsHjUaIL5uG9iKEXAUhh" + + "21H6PMgJE8CB5I6VjJ3Sj/tijcP9dH/f/h/aUtQ4pRixRCRz/s5ev3uB5ixWOVOz" + + "Uhga/NwjURlkg1Katg/14tvd8iZsAzI7KyXGdUefC8+Rulc8SBx+BqYIZ1FTLJkg" + + "obisps6esaaNZekvS0ECQQDxPnfWuNfwfpYFK038AhEFqBiOBMsMrB/QX+O0sQ/Z" + + "31gWkNuu3Z+cz6PG0FEK0O8/jsXQ1/gVAreADptJAMzpAkEA1N3GFV1LuYYqkp3a" + + "y4DnmdI/LgrBUAjWSvTSDKfB8Ik2Y8I2zDS20EsEQMvQYtLxMOMHIEqeAVzmnZcS" + + "dH4L/QJAQM/elFJuuU7Y8SSUO/s2JYXmqukAwDPSDEJmw5m6P3dwjAd47b7e7dsf" + + "Df/Tdgtx62ppHNWY8dQcBoxmDbPoWQJBANBQBBROS9fQA4Od9Usn5/5xcSDWp51y" + + "OHv8ID8AQNvq+44ets+aWrl2YEAk9NZxRlWoJwYj34LH7muUNxxHaeUCQQCjtLPU" + + "din4XjlMMRSTW/EOgX4JgZ+tQaAtB32wQocPDDeHqkDusUdlEpnDPbmkE9Sbxf1p" + + "0mSxXgnIuT99niBd"; + private static final String prkey2 = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIdcnQN/YkABlIDJ" + + "b2wqZgYEHRJReHvIWbeyNV7zJsMZdZRmQYKi2WMmNMH3DKyW7CCzdYrvHbQ5QyqP" + + "YaQ1X9hVv4bPzXPKSCBVPh5Cr13WwKgh3lhLmbkA/hJ9giRIo9jT47fHE3RaJkic" + + "RyCqWDSiOsnF7gNYJQf8MAarl/oTAgMBAAECgYAvZKvM57LcujDmjqCmfInQHWyY" + + "V1IZ9OIEwS6Smgu+T5GtbcgOFzqJzny2VOtpGkZ+hJbpGCOMUGhF5IHJXq65qvei" + + "ejBeJEH9xnTuJcXUKtHJjIGJQsKzuNKuTddFlY9an/BzoZq4/2Txyh2z6ZDZU1z0" + + "9pVY8tJe2FVyMY2oIQJBAKhLDMV+kfWLzDfiXijJ28DHdKCozaaAtLZRTRL/XlQU" + + "O8phcZ0wEKkzrNvn0Fn+5VIQFu1ghWvCo4iaS181FqkCQQDN5/+nbbzZ2AqQsBDB" + + "eCbLiXPJM7Ey69O4leMyzKL1dyI7nMl5/SeOQE/xBViqzF6M/VUGFzE1YOxl+HZA" + + "AQxbAkA3GNsFjXqbuTkS0rmhAPVBGAvGzX0GlfyT8+oX3iMzw6wPhfd8TtAllRVy" + + "qWI9FIZfZ4UaEg+cSAziHCGw+eCBAkAlLJWDbNSDn/WIWcEsrLyUUqSM1sBPKTR4" + + "RtdV/IwPNQsBKDVXyNSzYfkM0qJHc84+Nh57kwRXVsBXSJxQwd+xAkB9ZzCD0sp6" + + "8ISzFmbwL+uVtUbe/W0pLC9PDN9B7Vcw6Jf8V0B3lxB493rxTzG3o5oapDDxFIv/" + + "I6SViudhGXfP"; + + private static final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCHXJ0Df2JAAZSAyW9sKmYGBB0S" + + "UXh7yFm3sjVe8ybDGXWUZkGCotljJjTB9wysluwgs3WK7x20OUMqj2GkNV/YVb+G" + + "z81zykggVT4eQq9d1sCoId5YS5m5AP4SfYIkSKPY0+O3xxN0WiZInEcgqlg0ojrJ" + + "xe4DWCUH/DAGq5f6EwIDAQAB"; + + + + static RSA rsa2 = new RSA(prkey, null); + static RSA rsa22 = new RSA(prkey2, publicKey); + + public static String getIpAddress(HttpServletRequest request) { + // 获取请求的IP地址 + String ipAddress = request.getRemoteAddr(); + // 如果应用部署在反向代理或负载均衡器后面,可以通过X-Forwarded-For头部获取真实IP地址 + String forwardedFor = request.getHeader("X-Forwarded-For"); + if (forwardedFor != null && !forwardedFor.isEmpty()) { + ipAddress = forwardedFor.split(",")[0]; + } + return ipAddress; + } + + + /** + * 解密 + * @param str + * @return + */ + public static String decrypt(String str){ + + str= str.replaceAll(" ","+"); + byte[] decrypt2 = rsa2.decrypt(str, KeyType.PublicKey); + String s = new String(decrypt2); + String[] split = s.split(""); + s = split[0]; + return s; + } + + /** + * 加密 + * @param str + * @return + */ + public static String encryption(String str){ + return Base64.encode(rsa2.encrypt(str, KeyType.PrivateKey)); + } + + + /** + * 加密2 + * @param str + * @return + */ + public static String encryption2(String str){ + return Base64.encode(rsa22.encrypt(str, KeyType.PrivateKey)); + } + + /** + * 解密 + */ + public static String decrypt2(String str){ + //先进行base64解密 + str= Base64.decodeStr(str); + byte[] decrypt2 = rsa22.decrypt(str, KeyType.PublicKey); + String s = new String(decrypt2); + return s; + } + + +} diff --git a/旧的java项目/src/main/resources/application.properties b/旧的java项目/src/main/resources/application.properties new file mode 100644 index 0000000..e69de29 diff --git a/旧的java项目/src/main/resources/application.yml b/旧的java项目/src/main/resources/application.yml new file mode 100644 index 0000000..fddbd47 --- /dev/null +++ b/旧的java项目/src/main/resources/application.yml @@ -0,0 +1,78 @@ + +server: + port: 8651 +# ssl: +# key-store: classpath:yosin.jks +# key-store-type: JKS +# key-password: 2luolita + + + +#spring: +# datasource: +# dynamic: +# primary: master #??????????????,?????master +# strict: false #???????,??false. true?????????????,false??????? +# datasource: +# master: +# url: jdbc:mysql://127.0.0.1:3306/taiwan_cain_2nd?allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT +# username: game +# password: uu5!^%jg +# driver-class-name: com.mysql.jdbc.Driver # 3.2.0????SPI?????? + + +jwt: + secret: "rjyusrghdfghj2345ryu123asdfvbyukuirtwjhfsd" # 至少 32 位 + expiration: 7200 # 2 小时(单位:秒) +# +# +# +#spring: +# # 配置数据源信息 +# datasource: +# dynamic: +# # 设置默认的数据源或者数据源组,默认值即为master +# primary: master +# # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源 +# strict: false +# datasource: +# master: +# url: jdbc:mysql://192.168.200.25:3306/rindro?allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai +# username: root2 +# password: 527700 +# driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0????SPI?????? +#pro: +# configfile: "C:\\Users\\12134\\Desktop\\工作目录\\rindro\\Script\\FileConfig.json" +# userfile: "C:\\Users\\12134\\Desktop\\工作目录\\rindro\\" +# filemap: "C:\\Users\\12134\\Desktop\\工作目录\\rindro\\file.json" +# scriptfile: "C:\\Users\\12134\\Desktop\\工作目录\\rindro\\ClentScript\\" +# dps: "C:\\Users\\12134\\Desktop\\工作目录\\home\\DP_S\\" + +# +spring: + # 配置数据源信息 + datasource: + dynamic: + # 设置默认的数据源或者数据源组,默认值即为master + primary: master + # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源 + strict: false + datasource: + master: +# url: jdbc:mysql://110.40.74.134:3306/rindro?allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai +# username: root +# password: 527700 + url: jdbc:mysql://110.42.251.214:3306/rindro?allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai + username: root + password: Yosin0102! + driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0????SPI?????? +pro: + configfile: "/root/rindro/Script/FileConfig.json" + userfile: "/root/rindro/" + filemap: "/root/rindro/file.json" + scriptfile: "/root/rindro/ClentScript/" + dps: "/home/DP_S/" + +#mybatis-plus: +# configuration: +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 直接在控制台打印SQL \ No newline at end of file diff --git a/旧的java项目/src/main/resources/mapper/LogMapper.xml b/旧的java项目/src/main/resources/mapper/LogMapper.xml new file mode 100644 index 0000000..8ea0f27 --- /dev/null +++ b/旧的java项目/src/main/resources/mapper/LogMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/旧的java项目/src/main/resources/mapper/ProjectPerMapper.xml b/旧的java项目/src/main/resources/mapper/ProjectPerMapper.xml new file mode 100644 index 0000000..bf91147 --- /dev/null +++ b/旧的java项目/src/main/resources/mapper/ProjectPerMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/旧的java项目/src/main/resources/mapper/UserAndServerMapper.xml b/旧的java项目/src/main/resources/mapper/UserAndServerMapper.xml new file mode 100644 index 0000000..4b92e0c --- /dev/null +++ b/旧的java项目/src/main/resources/mapper/UserAndServerMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/旧的java项目/src/main/resources/mapper/UserMapper.xml b/旧的java项目/src/main/resources/mapper/UserMapper.xml new file mode 100644 index 0000000..054610f --- /dev/null +++ b/旧的java项目/src/main/resources/mapper/UserMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/旧的java项目/src/main/resources/mapper/UserServerProjectMapper.xml b/旧的java项目/src/main/resources/mapper/UserServerProjectMapper.xml new file mode 100644 index 0000000..192cb2e --- /dev/null +++ b/旧的java项目/src/main/resources/mapper/UserServerProjectMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/旧的java项目/src/main/resources/styles.css b/旧的java项目/src/main/resources/styles.css new file mode 100644 index 0000000..33ccf48 --- /dev/null +++ b/旧的java项目/src/main/resources/styles.css @@ -0,0 +1,45 @@ +.root { + -fx-font-family: "Microsoft YaHei", "Segoe UI", sans-serif; +} + +.button { + -fx-background-color: #4CAF50; + -fx-text-fill: white; + -fx-font-weight: bold; + -fx-background-radius: 5; + -fx-padding: 8 15; +} + +.button:hover { + -fx-background-color: #45a049; +} + +.button:disabled { + -fx-background-color: #cccccc; + -fx-text-fill: #666666; +} + +.text-field { + -fx-background-radius: 3; + -fx-border-color: #cccccc; + -fx-border-radius: 3; + -fx-padding: 5; +} + +.slider .track { + -fx-background-color: #e0e0e0; +} + +.slider .thumb { + -fx-background-color: #4CAF50; +} + +.progress-bar .track { + -fx-background-color: #e0e0e0; + -fx-background-radius: 2; +} + +.progress-bar .bar { + -fx-background-color: #4CAF50; + -fx-background-radius: 2; +} \ No newline at end of file diff --git a/旧的java项目/src/test/java/com/example/sjkbf/SjkbfApplicationTests.java b/旧的java项目/src/test/java/com/example/sjkbf/SjkbfApplicationTests.java new file mode 100644 index 0000000..99401cb --- /dev/null +++ b/旧的java项目/src/test/java/com/example/sjkbf/SjkbfApplicationTests.java @@ -0,0 +1,33 @@ +package com.example.sjkbf; + + +import cn.hutool.core.date.DateUtil; +import com.example.sjkbf.controller.AccountCargoController; +import com.example.sjkbf.entity.UserServerProject; +import com.example.sjkbf.service.UserServerProjectService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.*; + +@SpringBootTest +class SjkbfApplicationTests { + @Autowired + private UserServerProjectService service; + + + @Test + void contextLoads() { + List getpro = service.getpro("159.75.115.179"); + System.out.println(getpro); + } + + public static void main(String[] args) { + System.out.println(4462 - 589 + 140 - 100-150-106-106-60-50-98-100-65-30-50); + } + +}