commit 03229f23d40f015a33ffc8bee70ab996e4a4c14c Author: Lenheart <947330670@qq.com> Date: Wed Apr 15 15:19:28 2026 +0800 1.0 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 0000000..613597a Binary files /dev/null and b/build-debug-rebuild.log differ diff --git a/build-debug.log b/build-debug.log new file mode 100644 index 0000000..b306237 Binary files /dev/null and b/build-debug.log differ 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 0000000..c1dd12f Binary files /dev/null and b/旧的java项目/.mvn/wrapper/maven-wrapper.jar differ 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 0000000..d82feaf Binary files /dev/null and b/旧的java项目/src/main/java/com/example/sjkbf/service/SaticScheduleTask.class differ 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); + } + +}