Compare commits
2 Commits
713220ef8b
...
26b0e7ad7e
| Author | SHA1 | Date | |
|---|---|---|---|
| 26b0e7ad7e | |||
| 7f725c6e65 |
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/mvnw text eol=lf
|
||||||
|
*.cmd text eol=crlf
|
||||||
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@@ -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/
|
||||||
10
.idea/.gitignore
generated
vendored
Normal file
10
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 已忽略包含查询文件的默认文件夹
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# 基于编辑器的 HTTP 客户端请求
|
||||||
|
/httpRequests/
|
||||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||||
|
</state>
|
||||||
|
</component>
|
||||||
18
.idea/compiler.xml
generated
Normal file
18
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<annotationProcessing>
|
||||||
|
<profile name="Maven default annotation processors profile" enabled="true">
|
||||||
|
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||||
|
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||||
|
<outputRelativeToContentRoot value="true" />
|
||||||
|
<module name="pve-back-api" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
</component>
|
||||||
|
<component name="JavacSettings">
|
||||||
|
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
||||||
|
<module name="pve-back-api" options="-parameters" />
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/encodings.xml
generated
Normal file
6
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
23
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
23
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="VulnerableLibrariesLocal" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="isIgnoringEnabled" value="true" />
|
||||||
|
<option name="ignoredModules">
|
||||||
|
<list>
|
||||||
|
<option value="pve-back-api" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="ignoredPackages">
|
||||||
|
<list>
|
||||||
|
<option value="org.assertj:assertj-core:3.27.6" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="ignoredReasons">
|
||||||
|
<list>
|
||||||
|
<option value="进行中" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
20
.idea/jarRepositories.xml
generated
Normal file
20
.idea/jarRepositories.xml
generated
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RemoteRepositoriesConfiguration">
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Central Repository" />
|
||||||
|
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Maven Central repository" />
|
||||||
|
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="jboss.community" />
|
||||||
|
<option name="name" value="JBoss Community repository" />
|
||||||
|
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||||
|
</remote-repository>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
14
.idea/misc.xml
generated
Normal file
14
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="25" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
220
.idea/workspace.xml
generated
Normal file
220
.idea/workspace.xml
generated
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AutoImportSettings">
|
||||||
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
|
</component>
|
||||||
|
<component name="ChangeListManager">
|
||||||
|
<list default="true" id="b81ac005-9b3d-43a0-a86e-8b9849200d36" name="更改" comment="">
|
||||||
|
<change afterPath="$PROJECT_DIR$/.gitattributes" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/.gitignore" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/codeStyles/codeStyleConfig.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/compiler.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/encodings.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/inspectionProfiles/Project_Default.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/jarRepositories.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.mvn/wrapper/maven-wrapper.properties" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/HELP.md" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/conf/config.yaml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/examples.md" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/fuck-u-code" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/fuck-u-code.exe" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/mvnw" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/mvnw.cmd" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/top/gtb520/java/pve_back_api/Main.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/top/gtb520/java/pve_back_api/config/AppConfig.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/top/gtb520/java/pve_back_api/config/ConfigManager.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/top/gtb520/java/pve_back_api/config/GlobalConfig.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/top/gtb520/java/pve_back_api/config/YamlConfigLoader.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/top/gtb520/java/pve_back_api/route/pve/status.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/top/gtb520/java/pve_back_api/route/test.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/resources/application.properties" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/resources/config.yaml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/test/java/top/gtb520/java/pve_back_api/MainTests.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/test/java/top/gtb520/java/pve_back_api/ResourcesResultTest.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/classes/application.properties" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/classes/config.yaml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/classes/top/gtb520/java/pve_back_api/Main.class" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/classes/top/gtb520/java/pve_back_api/config/AppConfig.class" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/classes/top/gtb520/java/pve_back_api/config/ConfigManager.class" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/classes/top/gtb520/java/pve_back_api/config/GlobalConfig.class" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/classes/top/gtb520/java/pve_back_api/config/YamlConfigLoader.class" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/classes/top/gtb520/java/pve_back_api/route/pve/status.class" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/classes/top/gtb520/java/pve_back_api/route/test.class" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/surefire-reports/2026-02-04T18-14-48_524.dumpstream" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/surefire-reports/TEST-top.gtb520.java.pve_back_api.ResourcesResultTest.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/surefire-reports/top.gtb520.java.pve_back_api.ResourcesResultTest.txt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/test-classes/top/gtb520/java/pve_back_api/MainTests.class" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/target/test-classes/top/gtb520/java/pve_back_api/ResourcesResultTest.class" afterDir="false" />
|
||||||
|
</list>
|
||||||
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
|
</component>
|
||||||
|
<component name="ComposerSettings">
|
||||||
|
<execution />
|
||||||
|
</component>
|
||||||
|
<component name="FileTemplateManagerImpl">
|
||||||
|
<option name="RECENT_TEMPLATES">
|
||||||
|
<list>
|
||||||
|
<option value="Class" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="Git.Settings">
|
||||||
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
|
</component>
|
||||||
|
<component name="KubernetesApiPersistence">{}</component>
|
||||||
|
<component name="KubernetesApiProvider">{
|
||||||
|
"isMigrated": true
|
||||||
|
}</component>
|
||||||
|
<component name="MacroExpansionManager">
|
||||||
|
<option name="directoryName" value="IwU62d1q" />
|
||||||
|
</component>
|
||||||
|
<component name="MavenImportPreferences">
|
||||||
|
<option name="generalSettings">
|
||||||
|
<MavenGeneralSettings>
|
||||||
|
<option name="mavenHomeTypeForPersistence" value="WRAPPER" />
|
||||||
|
</MavenGeneralSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="MavenRunner">
|
||||||
|
<option name="skipTests" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PhpWorkspaceProjectConfiguration" interpreter_name="php-7.4" />
|
||||||
|
<component name="ProjectCodeStyleSettingsMigration">
|
||||||
|
<option name="version" value="2" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectColorInfo">{
|
||||||
|
"associatedIndex": 3
|
||||||
|
}</component>
|
||||||
|
<component name="ProjectId" id="397Avb8RVGvCmzdVVMJdi8C4ou0" />
|
||||||
|
<component name="ProjectViewState">
|
||||||
|
<option name="flattenModules" value="true" />
|
||||||
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
|
<option name="showLibraryContents" value="true" />
|
||||||
|
<option name="showMembers" value="true" />
|
||||||
|
<option name="showVisibilityIcons" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PropertiesComponent"><![CDATA[{
|
||||||
|
"keyToString": {
|
||||||
|
"JAR 应用程序.pve-back-api-0.0.1-SNAPSHOT.jar.executor": "Run",
|
||||||
|
"JUnit.ResourcesResultTest.executor": "Run",
|
||||||
|
"JUnit.ResourcesResultTest.test.executor": "Run",
|
||||||
|
"Maven.pve-back-api [clean].executor": "Run",
|
||||||
|
"Maven.pve-back-api [org.apache.maven.plugins:maven-install-plugin:3.1.4:install-file].executor": "Run",
|
||||||
|
"Maven.pve-back-api [org.apache.maven.plugins:maven-install-plugin:3.1.4:install].executor": "Run",
|
||||||
|
"Maven.pve-back-api [org.apache.maven.plugins:maven-jar-plugin:3.4.2:jar].executor": "Run",
|
||||||
|
"Maven.pve-back-api [package].executor": "Run",
|
||||||
|
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||||
|
"RequestMappingsPanelOrder0": "0",
|
||||||
|
"RequestMappingsPanelOrder1": "1",
|
||||||
|
"RequestMappingsPanelWidth0": "75",
|
||||||
|
"RequestMappingsPanelWidth1": "75",
|
||||||
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
|
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
|
||||||
|
"Spring Boot.Main.executor": "Run",
|
||||||
|
"git-widget-placeholder": "main",
|
||||||
|
"go.import.settings.migrated": "true",
|
||||||
|
"ignore.virus.scanning.warn.message": "true",
|
||||||
|
"kotlin-language-version-configured": "true",
|
||||||
|
"last_opened_file_path": "G:/Project/Java/pve-back-api",
|
||||||
|
"nodejs_package_manager_path": "npm",
|
||||||
|
"run.code.analysis.last.selected.profile": "pProject Default",
|
||||||
|
"settings.editor.selected.configurable": "preferences.pluginManager",
|
||||||
|
"vue.rearranger.settings.migration": "true"
|
||||||
|
}
|
||||||
|
}]]></component>
|
||||||
|
<component name="RecentsManager">
|
||||||
|
<key name="CopyFile.RECENT_KEYS">
|
||||||
|
<recent name="G:\Project\Java\pve-back-api" />
|
||||||
|
</key>
|
||||||
|
<key name="MoveClassesOrPackagesDialog.RECENTS_KEY">
|
||||||
|
<recent name="top.gtb520.java.pve_back_api.route" />
|
||||||
|
</key>
|
||||||
|
</component>
|
||||||
|
<component name="RunManager" selected="JUnit.ResourcesResultTest">
|
||||||
|
<configuration name="ResourcesResultTest" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
|
||||||
|
<module name="pve-back-api" />
|
||||||
|
<extension name="coverage">
|
||||||
|
<pattern>
|
||||||
|
<option name="PATTERN" value="top.gtb520.java.pve_back_api.*" />
|
||||||
|
<option name="ENABLED" value="true" />
|
||||||
|
</pattern>
|
||||||
|
</extension>
|
||||||
|
<option name="PACKAGE_NAME" value="top.gtb520.java.pve_back_api" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="top.gtb520.java.pve_back_api.ResourcesResultTest" />
|
||||||
|
<option name="TEST_OBJECT" value="class" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
<configuration name="ResourcesResultTest.test" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
|
||||||
|
<module name="pve-back-api" />
|
||||||
|
<extension name="coverage">
|
||||||
|
<pattern>
|
||||||
|
<option name="PATTERN" value="top.gtb520.java.pve_back_api.*" />
|
||||||
|
<option name="ENABLED" value="true" />
|
||||||
|
</pattern>
|
||||||
|
</extension>
|
||||||
|
<option name="PACKAGE_NAME" value="top.gtb520.java.pve_back_api" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="top.gtb520.java.pve_back_api.ResourcesResultTest" />
|
||||||
|
<option name="METHOD_NAME" value="test" />
|
||||||
|
<option name="TEST_OBJECT" value="method" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
<configuration name="pve-back-api-0.0.1-SNAPSHOT.jar" type="JarApplication" temporary="true">
|
||||||
|
<option name="JAR_PATH" value="$PROJECT_DIR$/target/pve-back-api-0.0.1-SNAPSHOT.jar" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<configuration name="Main (1)" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
|
||||||
|
<module name="pve-back-api" />
|
||||||
|
<option name="SPRING_BOOT_MAIN_CLASS" value="top.gtb520.java.pve_back_api.Main" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
<configuration name="Main" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
|
||||||
|
<module name="pve-back-api" />
|
||||||
|
<option name="SPRING_BOOT_MAIN_CLASS" value="top.gtb520.java.pve_back_api.main.Main" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
<recent_temporary>
|
||||||
|
<list>
|
||||||
|
<item itemvalue="JUnit.ResourcesResultTest" />
|
||||||
|
<item itemvalue="JUnit.ResourcesResultTest.test" />
|
||||||
|
<item itemvalue="JAR 应用程序.pve-back-api-0.0.1-SNAPSHOT.jar" />
|
||||||
|
</list>
|
||||||
|
</recent_temporary>
|
||||||
|
</component>
|
||||||
|
<component name="TaskManager">
|
||||||
|
<task active="true" id="Default" summary="默认任务">
|
||||||
|
<changelist id="b81ac005-9b3d-43a0-a86e-8b9849200d36" name="更改" comment="" />
|
||||||
|
<created>1770036968595</created>
|
||||||
|
<option name="number" value="Default" />
|
||||||
|
<option name="presentableId" value="Default" />
|
||||||
|
<updated>1770036968595</updated>
|
||||||
|
<workItem from="1770036971569" duration="418000" />
|
||||||
|
<workItem from="1770037429449" duration="210000" />
|
||||||
|
<workItem from="1770037658639" duration="8615000" />
|
||||||
|
<workItem from="1770086564958" duration="25373000" />
|
||||||
|
</task>
|
||||||
|
<servers />
|
||||||
|
</component>
|
||||||
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
<option name="version" value="3" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
3
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
3
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
wrapperVersion=3.3.4
|
||||||
|
distributionType=only-script
|
||||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip
|
||||||
32
HELP.md
Normal file
32
HELP.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Getting Started
|
||||||
|
|
||||||
|
### Reference Documentation
|
||||||
|
|
||||||
|
For further reference, please consider the following sections:
|
||||||
|
|
||||||
|
* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
|
||||||
|
* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/4.0.2/maven-plugin)
|
||||||
|
* [Create an OCI image](https://docs.spring.io/spring-boot/4.0.2/maven-plugin/build-image.html)
|
||||||
|
* [Spring Data Redis (Access+Driver)](https://docs.spring.io/spring-boot/4.0.2/reference/data/nosql.html#data.nosql.redis)
|
||||||
|
* [JDBC API](https://docs.spring.io/spring-boot/4.0.2/reference/data/sql.html)
|
||||||
|
* [Spring Shell](https://docs.spring.io/spring-shell/reference/index.html)
|
||||||
|
* [WebSocket](https://docs.spring.io/spring-boot/4.0.2/reference/messaging/websockets.html)
|
||||||
|
|
||||||
|
### Guides
|
||||||
|
|
||||||
|
The following guides illustrate how to use some features concretely:
|
||||||
|
|
||||||
|
* [Messaging with Redis](https://spring.io/guides/gs/messaging-redis/)
|
||||||
|
* [Accessing Relational Data using JDBC with Spring](https://spring.io/guides/gs/relational-data-access/)
|
||||||
|
* [Managing Transactions](https://spring.io/guides/gs/managing-transactions/)
|
||||||
|
* [Accessing data with MySQL](https://spring.io/guides/gs/accessing-data-mysql/)
|
||||||
|
* [Using WebSocket to build an interactive web application](https://spring.io/guides/gs/messaging-stomp-websocket/)
|
||||||
|
|
||||||
|
### Maven Parent overrides
|
||||||
|
|
||||||
|
Due to Maven's design, elements are inherited from the parent POM to the project POM.
|
||||||
|
While most of the inheritance is fine, it also inherits unwanted elements like `<license>` and `<developers>` from the
|
||||||
|
parent.
|
||||||
|
To prevent this, the project POM contains empty overrides for these elements.
|
||||||
|
If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides.
|
||||||
|
|
||||||
18
conf/config.yaml
Normal file
18
conf/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# 全局配置
|
||||||
|
global:
|
||||||
|
http_ip: "0.0.0.0"
|
||||||
|
http_port: "11554"
|
||||||
|
|
||||||
|
# 数据库配置
|
||||||
|
database:
|
||||||
|
mysql_jdbc: "jdbc:mysql://10.168.2.2:3306/pve_monitor?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalInfile=true&allowUrlInLocalInfile=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&allowPublicKeyRetrieval=true&allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&allowPublicKeyRetrieval=true&allowLoadLocalInfile=true&allowUrlInLocal"
|
||||||
|
mysql_username: "root"
|
||||||
|
mysql_password: "Password"
|
||||||
|
|
||||||
|
# PVE配置
|
||||||
|
pve:
|
||||||
|
# PVE后端地址,示例:https://192.168.1.1:8006/ (协议HTTPS,带结尾的“/”,不保留结尾其他参数)
|
||||||
|
url: "https://10.168.2.18:8006/"
|
||||||
|
api_tocken_name: "dev"
|
||||||
|
api_token_id: "root@pam!dev"
|
||||||
|
api_token_secret: "68e9dac5-3110-4f1b-b33a-feeaa4014f63"
|
||||||
431
examples.md
Normal file
431
examples.md
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
# Basic Examples
|
||||||
|
|
||||||
|
This guide provides common usage patterns and practical examples for getting started with the Proxmox VE API.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### **Basic Connection**
|
||||||
|
|
||||||
|
```java
|
||||||
|
import it.corsinvest.proxmoxve.api.*;
|
||||||
|
|
||||||
|
// Create client and authenticate
|
||||||
|
var client = new PveClient("pve.example.com", 8006);
|
||||||
|
client.setApiToken("user@pve!token=uuid");
|
||||||
|
|
||||||
|
// Test connection
|
||||||
|
var version = client.getVersion().version();
|
||||||
|
if (version.isSuccessStatusCode()) {
|
||||||
|
System.out.println("Connected to Proxmox VE " +
|
||||||
|
version.getResponse().get("data").get("version").asText());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Client Setup with Error Handling**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static PveClient createClient() {
|
||||||
|
var client = new PveClient("pve.local", 8006);
|
||||||
|
client.setValidateCertificate(false); // For development
|
||||||
|
client.setTimeout(120000); // 2 minutes
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Use API token or login
|
||||||
|
String token = System.getenv("PVE_TOKEN");
|
||||||
|
if (token != null && !token.isEmpty()) {
|
||||||
|
client.setApiToken(token);
|
||||||
|
} else {
|
||||||
|
boolean success = client.login("root@pam", "password");
|
||||||
|
if (!success) {
|
||||||
|
throw new Exception("Authentication failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return client;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.out.println("Failed to create client: " + ex.getMessage());
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Virtual Machine Operations
|
||||||
|
|
||||||
|
### **List Virtual Machines**
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Get all VMs in cluster
|
||||||
|
var resources = client.getCluster().getResources().resources().getData();
|
||||||
|
|
||||||
|
for (JsonNode resource : resources) {
|
||||||
|
if ("qemu".equals(resource.get("type").asText())) {
|
||||||
|
System.out.printf("VM %d: %s on %s - %s%n",
|
||||||
|
resource.get("vmid").asInt(),
|
||||||
|
resource.get("name").asText(),
|
||||||
|
resource.get("node").asText(),
|
||||||
|
resource.get("status").asText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter running VMs
|
||||||
|
var runningVms = new ArrayList<JsonNode>();
|
||||||
|
for (JsonNode resource : resources) {
|
||||||
|
if ("qemu".equals(resource.get("type").asText()) &&
|
||||||
|
"running".equals(resource.get("status").asText())) {
|
||||||
|
runningVms.add(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("Running VMs: " + runningVms.size());
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Get VM Configuration**
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Get VM configuration
|
||||||
|
var config = client.getNodes().get("pve1").getQemu().get(100)
|
||||||
|
.getConfig().vmConfig().getData();
|
||||||
|
|
||||||
|
System.out.println("VM Name: " + config.get("name").asText());
|
||||||
|
System.out.println("Memory: " + config.get("memory").asInt() + " MB");
|
||||||
|
System.out.println("CPU Cores: " + config.get("cores").asInt());
|
||||||
|
System.out.println("Boot Order: " + config.get("boot").asText());
|
||||||
|
```
|
||||||
|
|
||||||
|
### **VM Power Management**
|
||||||
|
|
||||||
|
```java
|
||||||
|
var vm = client.getNodes().get("pve1").getQemu().get(100);
|
||||||
|
|
||||||
|
// Start VM
|
||||||
|
vm.getStatus().start();
|
||||||
|
System.out.println("VM started successfully");
|
||||||
|
|
||||||
|
// Stop VM
|
||||||
|
vm.getStatus().stop();
|
||||||
|
System.out.println("VM stopped successfully");
|
||||||
|
|
||||||
|
// Restart VM
|
||||||
|
vm.getStatus().reboot();
|
||||||
|
System.out.println("VM restarted successfully");
|
||||||
|
|
||||||
|
// Get current status
|
||||||
|
var data = vm.getStatus().current().getData();
|
||||||
|
System.out.println("VM Status: " + data.get("status").asText());
|
||||||
|
System.out.printf("CPU Usage: %.2f%%%n", data.get("cpu").asDouble() * 100);
|
||||||
|
System.out.printf("Memory: %.2f%%%n",
|
||||||
|
(data.get("mem").asDouble() / data.get("maxmem").asDouble()) * 100);
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Snapshot Management**
|
||||||
|
|
||||||
|
```java
|
||||||
|
var vm = client.getNodes().get("pve1").getQemu().get(100);
|
||||||
|
|
||||||
|
// Create snapshot
|
||||||
|
vm.getSnapshot().snapshot("backup-2024", "Pre-update backup");
|
||||||
|
System.out.println("Snapshot created successfully");
|
||||||
|
|
||||||
|
// List snapshots
|
||||||
|
var snapshots = vm.getSnapshot().snapshotList().getData();
|
||||||
|
System.out.println("Available snapshots:");
|
||||||
|
for (JsonNode snapshot : snapshots) {
|
||||||
|
System.out.printf(" - %s: %s (%s)%n",
|
||||||
|
snapshot.get("name").asText(),
|
||||||
|
snapshot.get("description").asText(),
|
||||||
|
snapshot.get("snaptime").asText());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore snapshot
|
||||||
|
vm.getSnapshot().get("backup-2024").rollback();
|
||||||
|
System.out.println("Snapshot restored successfully");
|
||||||
|
|
||||||
|
// Delete snapshot
|
||||||
|
vm.getSnapshot().get("backup-2024").delsnapshot();
|
||||||
|
System.out.println("Snapshot deleted successfully");
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Container Operations
|
||||||
|
|
||||||
|
### **List Containers**
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Get all containers
|
||||||
|
var resources = client.getCluster().getResources().resources().getData();
|
||||||
|
|
||||||
|
for (JsonNode resource : resources) {
|
||||||
|
if ("lxc".equals(resource.get("type").asText())) {
|
||||||
|
System.out.printf("CT %d: %s on %s - %s%n",
|
||||||
|
resource.get("vmid").asInt(),
|
||||||
|
resource.get("name").asText(),
|
||||||
|
resource.get("node").asText(),
|
||||||
|
resource.get("status").asText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Container Management**
|
||||||
|
|
||||||
|
```java
|
||||||
|
var container = client.getNodes().get("pve1").getLxc().get(101);
|
||||||
|
|
||||||
|
// Get container configuration
|
||||||
|
var ctConfig = container.getConfig().vmConfig().getData();
|
||||||
|
System.out.println("Container: " + ctConfig.get("hostname").asText());
|
||||||
|
System.out.println("OS Template: " + ctConfig.get("ostemplate").asText());
|
||||||
|
System.out.println("Memory: " + ctConfig.get("memory").asInt() + " MB");
|
||||||
|
|
||||||
|
// Start container
|
||||||
|
container.getStatus().start();
|
||||||
|
System.out.println("Container started");
|
||||||
|
|
||||||
|
// Get container status
|
||||||
|
var data = container.getStatus().current().getData();
|
||||||
|
System.out.println("Status: " + data.get("status").asText());
|
||||||
|
System.out.println("Uptime: " + data.get("uptime").asInt() + " seconds");
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cluster Operations
|
||||||
|
|
||||||
|
### **Cluster Status**
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Get cluster status
|
||||||
|
var status = client.getCluster().getStatus().getStatus().getData();
|
||||||
|
System.out.println("Cluster Status:");
|
||||||
|
for (JsonNode item : status) {
|
||||||
|
System.out.printf(" %s: %s - %s%n",
|
||||||
|
item.get("type").asText(),
|
||||||
|
item.get("name").asText(),
|
||||||
|
item.get("status").asText());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Node Information**
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Get all nodes
|
||||||
|
var nodes = client.getNodes().index().getData();
|
||||||
|
System.out.println("Available Nodes:");
|
||||||
|
for (JsonNode node : nodes) {
|
||||||
|
System.out.println(" " + node.get("node").asText() + ": " +
|
||||||
|
node.get("status").asText());
|
||||||
|
System.out.printf(" CPU: %.2f%%%n", node.get("cpu").asDouble() * 100);
|
||||||
|
System.out.printf(" Memory: %.2f%%%n",
|
||||||
|
(node.get("mem").asDouble() / node.get("maxmem").asDouble()) * 100);
|
||||||
|
System.out.println(" Uptime: " +
|
||||||
|
java.time.Duration.ofSeconds(node.get("uptime").asInt()));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Storage Information**
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Get storage for a specific node
|
||||||
|
var storages = client.getNodes().get("pve1").getStorage().index().getData();
|
||||||
|
System.out.println("Available Storage:");
|
||||||
|
for (JsonNode storage : storages) {
|
||||||
|
double usedPercent = (storage.get("used").asDouble() /
|
||||||
|
storage.get("total").asDouble()) * 100;
|
||||||
|
System.out.printf(" %s (%s): %.1f%% used%n",
|
||||||
|
storage.get("storage").asText(),
|
||||||
|
storage.get("type").asText(),
|
||||||
|
usedPercent);
|
||||||
|
System.out.printf(" Total: %.2f GB%n",
|
||||||
|
storage.get("total").asLong() / (1024.0 * 1024 * 1024));
|
||||||
|
System.out.printf(" Available: %.2f GB%n",
|
||||||
|
storage.get("avail").asLong() / (1024.0 * 1024 * 1024));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### **Resource Monitoring**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static void monitorResources(PveClient client) throws InterruptedException {
|
||||||
|
while (true) {
|
||||||
|
var resources = client.getCluster().getResources().resources().getData();
|
||||||
|
|
||||||
|
System.out.print("\033[H\033[2J"); // Clear console
|
||||||
|
System.out.flush();
|
||||||
|
System.out.println("Proxmox VE Resource Monitor - " +
|
||||||
|
java.time.LocalTime.now());
|
||||||
|
System.out.println("=".repeat(50));
|
||||||
|
|
||||||
|
// Count by type
|
||||||
|
int nodeCount = 0, vmCount = 0, ctCount = 0;
|
||||||
|
int runningVms = 0, runningCts = 0;
|
||||||
|
|
||||||
|
for (JsonNode resource : resources) {
|
||||||
|
String type = resource.get("type").asText();
|
||||||
|
String status = resource.get("status").asText();
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "node":
|
||||||
|
nodeCount++;
|
||||||
|
System.out.printf(" %s: CPU %.1f%%, Memory %.1f%%%n",
|
||||||
|
resource.get("node").asText(),
|
||||||
|
resource.get("cpu").asDouble() * 100,
|
||||||
|
(resource.get("mem").asDouble() /
|
||||||
|
resource.get("maxmem").asDouble()) * 100);
|
||||||
|
break;
|
||||||
|
case "qemu":
|
||||||
|
vmCount++;
|
||||||
|
if ("running".equals(status)) runningVms++;
|
||||||
|
break;
|
||||||
|
case "lxc":
|
||||||
|
ctCount++;
|
||||||
|
if ("running".equals(status)) runningCts++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.printf("\nNodes: %d%n", nodeCount);
|
||||||
|
System.out.printf("VMs: %d (%d running)%n", vmCount, runningVms);
|
||||||
|
System.out.printf("Containers: %d (%d running)%n", ctCount, runningCts);
|
||||||
|
|
||||||
|
Thread.sleep(5000); // Update every 5 seconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Batch Operations**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static void batchVmOperation(PveClient client, int[] vmIds, String operation) {
|
||||||
|
var resources = client.getCluster().getResources().resources().getData();
|
||||||
|
|
||||||
|
for (int vmId : vmIds) {
|
||||||
|
// Find VM location
|
||||||
|
JsonNode vmResource = null;
|
||||||
|
for (JsonNode resource : resources) {
|
||||||
|
if ("qemu".equals(resource.get("type").asText()) &&
|
||||||
|
resource.get("vmid").asInt() == vmId) {
|
||||||
|
vmResource = resource;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vmResource != null) {
|
||||||
|
String node = vmResource.get("node").asText();
|
||||||
|
var vm = client.getNodes().get(node).getQemu().get(vmId);
|
||||||
|
|
||||||
|
Result result = switch (operation.toLowerCase()) {
|
||||||
|
case "start" -> vm.getStatus().start();
|
||||||
|
case "stop" -> vm.getStatus().stop();
|
||||||
|
case "restart" -> vm.getStatus().reboot();
|
||||||
|
default -> throw new IllegalArgumentException("Unknown operation: " + operation);
|
||||||
|
};
|
||||||
|
|
||||||
|
boolean success = result.isSuccessStatusCode();
|
||||||
|
System.out.printf("VM %d %s: %s%n",
|
||||||
|
vmId, operation, success ? "Success" : "Failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Performance Monitoring**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static void getVmPerformance(PveClient client, String node, int vmId) {
|
||||||
|
var data = client.getNodes().get(node).getQemu().get(vmId)
|
||||||
|
.getStatus().current().getData();
|
||||||
|
|
||||||
|
System.out.println("VM " + vmId + " Performance:");
|
||||||
|
System.out.println(" Status: " + data.get("status").asText());
|
||||||
|
System.out.printf(" CPU Usage: %.2f%%%n", data.get("cpu").asDouble() * 100);
|
||||||
|
System.out.printf(" Memory: %.2f GB / %.2f GB (%.1f%%)%n",
|
||||||
|
data.get("mem").asLong() / (1024.0 * 1024 * 1024),
|
||||||
|
data.get("maxmem").asLong() / (1024.0 * 1024 * 1024),
|
||||||
|
(data.get("mem").asDouble() / data.get("maxmem").asDouble()) * 100);
|
||||||
|
System.out.printf(" Disk Read: %.2f MB%n",
|
||||||
|
data.get("diskread").asLong() / (1024.0 * 1024));
|
||||||
|
System.out.printf(" Disk Write: %.2f MB%n",
|
||||||
|
data.get("diskwrite").asLong() / (1024.0 * 1024));
|
||||||
|
System.out.printf(" Network In: %.2f MB%n",
|
||||||
|
data.get("netin").asLong() / (1024.0 * 1024));
|
||||||
|
System.out.printf(" Network Out: %.2f MB%n",
|
||||||
|
data.get("netout").asLong() / (1024.0 * 1024));
|
||||||
|
System.out.println(" Uptime: " +
|
||||||
|
java.time.Duration.ofSeconds(data.get("uptime").asInt()));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### **Error Handling**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static boolean safeVmOperation(PveClient client, String node, int vmId, String operation) {
|
||||||
|
try {
|
||||||
|
var vm = client.getNodes().get(node).getQemu().get(vmId);
|
||||||
|
|
||||||
|
Result result = switch (operation.toLowerCase()) {
|
||||||
|
case "start" -> vm.getStatus().start();
|
||||||
|
case "stop" -> vm.getStatus().stop();
|
||||||
|
default -> throw new IllegalArgumentException("Unknown operation: " + operation);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (result.isSuccessStatusCode()) {
|
||||||
|
System.out.println("VM " + vmId + " " + operation + " successful");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
System.out.println("VM " + vmId + " " + operation + " failed: " +
|
||||||
|
result.getError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.out.println("Exception during " + operation + " on VM " + vmId + ": " +
|
||||||
|
ex.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Resource Discovery**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static class VmLocation {
|
||||||
|
public String node;
|
||||||
|
public int vmId;
|
||||||
|
|
||||||
|
public VmLocation(String node, int vmId) {
|
||||||
|
this.node = node;
|
||||||
|
this.vmId = vmId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VmLocation findVm(PveClient client, String vmName) {
|
||||||
|
var resources = client.getCluster().getResources().resources().getData();
|
||||||
|
|
||||||
|
for (JsonNode resource : resources) {
|
||||||
|
if ("qemu".equals(resource.get("type").asText()) &&
|
||||||
|
vmName.equalsIgnoreCase(resource.get("name").asText())) {
|
||||||
|
return new VmLocation(
|
||||||
|
resource.get("node").asText(),
|
||||||
|
resource.get("vmid").asInt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
var vmLocation = findVm(client, "web-server");
|
||||||
|
if (vmLocation != null) {
|
||||||
|
var vm = client.getNodes().get(vmLocation.node).getQemu().get(vmLocation.vmId);
|
||||||
|
// ... work with VM
|
||||||
|
}
|
||||||
|
```
|
||||||
1
fuck-u-code
Normal file
1
fuck-u-code
Normal file
@@ -0,0 +1 @@
|
|||||||
|
unknown shorthand flag: 'G' in -G:\Project\Java\pve-back-api
|
||||||
BIN
fuck-u-code.exe
Normal file
BIN
fuck-u-code.exe
Normal file
Binary file not shown.
295
mvnw
vendored
Normal file
295
mvnw
vendored
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
#!/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
|
||||||
|
#
|
||||||
|
# http://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.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Apache Maven Wrapper startup batch script, version 3.3.4
|
||||||
|
#
|
||||||
|
# Optional ENV vars
|
||||||
|
# -----------------
|
||||||
|
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||||
|
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
|
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
|
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
set -euf
|
||||||
|
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
||||||
|
|
||||||
|
# OS specific support.
|
||||||
|
native_path() { printf %s\\n "$1"; }
|
||||||
|
case "$(uname)" in
|
||||||
|
CYGWIN* | MINGW*)
|
||||||
|
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||||
|
native_path() { cygpath --path --windows "$1"; }
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# set JAVACMD and JAVACCMD
|
||||||
|
set_java_home() {
|
||||||
|
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||||
|
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"
|
||||||
|
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||||
|
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||||
|
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="$(
|
||||||
|
'set' +e
|
||||||
|
'unset' -f command 2>/dev/null
|
||||||
|
'command' -v java
|
||||||
|
)" || :
|
||||||
|
JAVACCMD="$(
|
||||||
|
'set' +e
|
||||||
|
'unset' -f command 2>/dev/null
|
||||||
|
'command' -v javac
|
||||||
|
)" || :
|
||||||
|
|
||||||
|
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||||
|
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# hash string like Java String::hashCode
|
||||||
|
hash_string() {
|
||||||
|
str="${1:-}" h=0
|
||||||
|
while [ -n "$str" ]; do
|
||||||
|
char="${str%"${str#?}"}"
|
||||||
|
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
||||||
|
str="${str#?}"
|
||||||
|
done
|
||||||
|
printf %x\\n $h
|
||||||
|
}
|
||||||
|
|
||||||
|
verbose() { :; }
|
||||||
|
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
||||||
|
|
||||||
|
die() {
|
||||||
|
printf %s\\n "$1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
trim() {
|
||||||
|
# MWRAPPER-139:
|
||||||
|
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
||||||
|
# Needed for removing poorly interpreted newline sequences when running in more
|
||||||
|
# exotic environments such as mingw bash on Windows.
|
||||||
|
printf "%s" "${1}" | tr -d '[:space:]'
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptDir="$(dirname "$0")"
|
||||||
|
scriptName="$(basename "$0")"
|
||||||
|
|
||||||
|
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
|
while IFS="=" read -r key value; do
|
||||||
|
case "${key-}" in
|
||||||
|
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
||||||
|
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
||||||
|
esac
|
||||||
|
done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
|
||||||
|
case "${distributionUrl##*/}" in
|
||||||
|
maven-mvnd-*bin.*)
|
||||||
|
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
||||||
|
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
||||||
|
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
||||||
|
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
||||||
|
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
||||||
|
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
||||||
|
*)
|
||||||
|
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
||||||
|
distributionPlatform=linux-amd64
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
||||||
|
;;
|
||||||
|
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
||||||
|
*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||||
|
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
||||||
|
distributionUrlName="${distributionUrl##*/}"
|
||||||
|
distributionUrlNameMain="${distributionUrlName%.*}"
|
||||||
|
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
||||||
|
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
|
||||||
|
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
||||||
|
|
||||||
|
exec_maven() {
|
||||||
|
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
||||||
|
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -d "$MAVEN_HOME" ]; then
|
||||||
|
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||||
|
exec_maven "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${distributionUrl-}" in
|
||||||
|
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
||||||
|
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# prepare tmp dir
|
||||||
|
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
||||||
|
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
||||||
|
trap clean HUP INT TERM EXIT
|
||||||
|
else
|
||||||
|
die "cannot create temp dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p -- "${MAVEN_HOME%/*}"
|
||||||
|
|
||||||
|
# Download and Install Apache Maven
|
||||||
|
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||||
|
verbose "Downloading from: $distributionUrl"
|
||||||
|
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
|
||||||
|
# select .zip or .tar.gz
|
||||||
|
if ! command -v unzip >/dev/null; then
|
||||||
|
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
||||||
|
distributionUrlName="${distributionUrl##*/}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# verbose opt
|
||||||
|
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
||||||
|
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
||||||
|
|
||||||
|
# normalize http auth
|
||||||
|
case "${MVNW_PASSWORD:+has-password}" in
|
||||||
|
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||||
|
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
||||||
|
verbose "Found wget ... using wget"
|
||||||
|
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
||||||
|
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
||||||
|
verbose "Found curl ... using curl"
|
||||||
|
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
||||||
|
elif set_java_home; then
|
||||||
|
verbose "Falling back to use Java to download"
|
||||||
|
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
||||||
|
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
cat >"$javaSource" <<-END
|
||||||
|
public class Downloader extends java.net.Authenticator
|
||||||
|
{
|
||||||
|
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
||||||
|
{
|
||||||
|
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
||||||
|
}
|
||||||
|
public static void main( String[] args ) throws Exception
|
||||||
|
{
|
||||||
|
setDefault( new Downloader() );
|
||||||
|
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END
|
||||||
|
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
||||||
|
verbose " - Compiling Downloader.java ..."
|
||||||
|
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
||||||
|
verbose " - Running Downloader.java ..."
|
||||||
|
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||||
|
if [ -n "${distributionSha256Sum-}" ]; then
|
||||||
|
distributionSha256Result=false
|
||||||
|
if [ "$MVN_CMD" = mvnd.sh ]; then
|
||||||
|
echo "Checksum validation is not supported for maven-mvnd." >&2
|
||||||
|
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||||
|
exit 1
|
||||||
|
elif command -v sha256sum >/dev/null; then
|
||||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
|
||||||
|
distributionSha256Result=true
|
||||||
|
fi
|
||||||
|
elif command -v shasum >/dev/null; then
|
||||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||||
|
distributionSha256Result=true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||||
|
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ $distributionSha256Result = false ]; then
|
||||||
|
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
||||||
|
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# unzip and move
|
||||||
|
if command -v unzip >/dev/null; then
|
||||||
|
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
||||||
|
else
|
||||||
|
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
||||||
|
actualDistributionDir=""
|
||||||
|
|
||||||
|
# First try the expected directory name (for regular distributions)
|
||||||
|
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
|
||||||
|
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
|
||||||
|
actualDistributionDir="$distributionUrlNameMain"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If not found, search for any directory with the Maven executable (for snapshots)
|
||||||
|
if [ -z "$actualDistributionDir" ]; then
|
||||||
|
# enable globbing to iterate over items
|
||||||
|
set +f
|
||||||
|
for dir in "$TMP_DOWNLOAD_DIR"/*; do
|
||||||
|
if [ -d "$dir" ]; then
|
||||||
|
if [ -f "$dir/bin/$MVN_CMD" ]; then
|
||||||
|
actualDistributionDir="$(basename "$dir")"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
set -f
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$actualDistributionDir" ]; then
|
||||||
|
verbose "Contents of $TMP_DOWNLOAD_DIR:"
|
||||||
|
verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
|
||||||
|
die "Could not find Maven distribution directory in extracted archive"
|
||||||
|
fi
|
||||||
|
|
||||||
|
verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
||||||
|
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
|
||||||
|
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||||
|
|
||||||
|
clean || :
|
||||||
|
exec_maven "$@"
|
||||||
189
mvnw.cmd
vendored
Normal file
189
mvnw.cmd
vendored
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
<# : batch portion
|
||||||
|
@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 http://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 Apache Maven Wrapper startup batch script, version 3.3.4
|
||||||
|
@REM
|
||||||
|
@REM Optional ENV vars
|
||||||
|
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
|
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
|
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||||
|
@SET __MVNW_CMD__=
|
||||||
|
@SET __MVNW_ERROR__=
|
||||||
|
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||||
|
@SET PSModulePath=
|
||||||
|
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||||
|
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||||
|
)
|
||||||
|
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||||
|
@SET __MVNW_PSMODULEP_SAVE=
|
||||||
|
@SET __MVNW_ARG0_NAME__=
|
||||||
|
@SET MVNW_USERNAME=
|
||||||
|
@SET MVNW_PASSWORD=
|
||||||
|
@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
|
||||||
|
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||||
|
@GOTO :EOF
|
||||||
|
: end batch / begin powershell #>
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
if ($env:MVNW_VERBOSE -eq "true") {
|
||||||
|
$VerbosePreference = "Continue"
|
||||||
|
}
|
||||||
|
|
||||||
|
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
|
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||||
|
if (!$distributionUrl) {
|
||||||
|
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||||
|
"maven-mvnd-*" {
|
||||||
|
$USE_MVND = $true
|
||||||
|
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||||
|
$MVN_CMD = "mvnd.cmd"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
$USE_MVND = $false
|
||||||
|
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||||
|
if ($env:MVNW_REPOURL) {
|
||||||
|
$MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||||
|
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
|
||||||
|
}
|
||||||
|
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||||
|
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||||
|
|
||||||
|
$MAVEN_M2_PATH = "$HOME/.m2"
|
||||||
|
if ($env:MAVEN_USER_HOME) {
|
||||||
|
$MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
|
||||||
|
New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
$MAVEN_WRAPPER_DISTS = $null
|
||||||
|
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
|
||||||
|
$MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
|
||||||
|
} else {
|
||||||
|
$MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
|
||||||
|
}
|
||||||
|
|
||||||
|
$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
|
||||||
|
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||||
|
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||||
|
|
||||||
|
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||||
|
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||||
|
exit $?
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||||
|
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||||
|
}
|
||||||
|
|
||||||
|
# prepare tmp dir
|
||||||
|
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||||
|
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||||
|
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||||
|
trap {
|
||||||
|
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||||
|
|
||||||
|
# Download and Install Apache Maven
|
||||||
|
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||||
|
Write-Verbose "Downloading from: $distributionUrl"
|
||||||
|
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
|
||||||
|
$webclient = New-Object System.Net.WebClient
|
||||||
|
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||||
|
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||||
|
}
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
|
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||||
|
|
||||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||||
|
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||||
|
if ($distributionSha256Sum) {
|
||||||
|
if ($USE_MVND) {
|
||||||
|
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||||
|
}
|
||||||
|
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||||
|
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||||
|
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# unzip and move
|
||||||
|
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||||
|
|
||||||
|
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
||||||
|
$actualDistributionDir = ""
|
||||||
|
|
||||||
|
# First try the expected directory name (for regular distributions)
|
||||||
|
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
|
||||||
|
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
|
||||||
|
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
|
||||||
|
$actualDistributionDir = $distributionUrlNameMain
|
||||||
|
}
|
||||||
|
|
||||||
|
# If not found, search for any directory with the Maven executable (for snapshots)
|
||||||
|
if (!$actualDistributionDir) {
|
||||||
|
Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
|
||||||
|
$testPath = Join-Path $_.FullName "bin/$MVN_CMD"
|
||||||
|
if (Test-Path -Path $testPath -PathType Leaf) {
|
||||||
|
$actualDistributionDir = $_.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$actualDistributionDir) {
|
||||||
|
Write-Error "Could not find Maven distribution directory in extracted archive"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
||||||
|
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||||
|
try {
|
||||||
|
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||||
|
} catch {
|
||||||
|
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||||
|
Write-Error "fail to move MAVEN_HOME"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||||
91
pom.xml
Normal file
91
pom.xml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>4.0.2</version>
|
||||||
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>top.gtb520.java.pve_back_api.main</groupId>
|
||||||
|
<artifactId>pve-back-api</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>pve-back-api</name>
|
||||||
|
<description>pve-back-api</description>
|
||||||
|
<url/>
|
||||||
|
<licenses>
|
||||||
|
<license/>
|
||||||
|
</licenses>
|
||||||
|
<developers>
|
||||||
|
<developer/>
|
||||||
|
</developers>
|
||||||
|
<scm>
|
||||||
|
<connection/>
|
||||||
|
<developerConnection/>
|
||||||
|
<tag/>
|
||||||
|
<url/>
|
||||||
|
</scm>
|
||||||
|
<properties>
|
||||||
|
<java.version>25</java.version>
|
||||||
|
<spring-shell.version>4.0.1</spring-shell.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-webmvc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- YAML解析依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.yaml</groupId>
|
||||||
|
<artifactId>snakeyaml</artifactId>
|
||||||
|
<version>2.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 日志依赖(可选,Spring Boot已包含) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 连接后端PVE的依赖-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>it.corsinvest.proxmoxve</groupId>
|
||||||
|
<artifactId>cv4pve-api-java</artifactId>
|
||||||
|
<version>9.1.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
99
src/main/java/top/gtb520/java/pve_back_api/Main.java
Normal file
99
src/main/java/top/gtb520/java/pve_back_api/Main.java
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package top.gtb520.java.pve_back_api;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import top.gtb520.java.pve_back_api.config.AppConfig;
|
||||||
|
import top.gtb520.java.pve_back_api.config.ConfigManager;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("=== PVE后端API服务启动 ===");
|
||||||
|
|
||||||
|
// 初始化配置系统
|
||||||
|
initializeConfiguration();
|
||||||
|
|
||||||
|
// 执行初始化
|
||||||
|
Main main = new Main();
|
||||||
|
main.postInitialization();
|
||||||
|
|
||||||
|
// 启动Spring Boot应用
|
||||||
|
SpringApplication app = new SpringApplication(Main.class);
|
||||||
|
app.setDefaultProperties(Collections.singletonMap("server.port", AppConfig.HTTP_PORT));
|
||||||
|
app.run(args);
|
||||||
|
System.out.println("Spring框架加载完成!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化配置系统
|
||||||
|
*/
|
||||||
|
private static void initializeConfiguration() {
|
||||||
|
try {
|
||||||
|
String configPath = ConfigManager.initializeConfig();
|
||||||
|
if (configPath != null) {
|
||||||
|
System.out.println("配置系统初始化成功: " + configPath);
|
||||||
|
|
||||||
|
// 验证配置完整性
|
||||||
|
if (!AppConfig.validateRequiredConfig()) {
|
||||||
|
System.err.println("警告: 必要配置项不完整,请检查配置文件");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.err.println("配置系统初始化失败!");
|
||||||
|
// 使用默认配置继续运行
|
||||||
|
AppConfig.markAsLoaded();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("配置初始化异常: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
// 即使配置失败也继续启动应用
|
||||||
|
AppConfig.markAsLoaded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用启动后的初始化工作
|
||||||
|
*/
|
||||||
|
public void postInitialization() {
|
||||||
|
System.out.println("=== 应用初始化 ===");
|
||||||
|
|
||||||
|
if (AppConfig.isConfigLoaded()) {
|
||||||
|
System.out.println("配置状态: 已加载");
|
||||||
|
|
||||||
|
// 显示关键配置信息
|
||||||
|
System.out.println("服务地址: " + AppConfig.HTTP_IP + ":" + AppConfig.HTTP_PORT);
|
||||||
|
System.out.println("PVE地址: " + AppConfig.PVE_URL);
|
||||||
|
|
||||||
|
// 可以在这里添加其他初始化逻辑
|
||||||
|
performAdditionalInitialization();
|
||||||
|
} else {
|
||||||
|
System.out.println("配置状态: 未加载,使用默认配置");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("应用初始化完成!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行额外的初始化任务
|
||||||
|
*/
|
||||||
|
private void performAdditionalInitialization() {
|
||||||
|
// 这里可以添加数据库连接测试、PVE连接验证等
|
||||||
|
try {
|
||||||
|
// 示例:测试数据库连接配置
|
||||||
|
if (AppConfig.MYSQL_JDBC != null && !AppConfig.MYSQL_JDBC.isEmpty()) {
|
||||||
|
System.out.println("数据库配置已设置");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 示例:测试PVE配置
|
||||||
|
if (AppConfig.PVE_URL != null && !AppConfig.PVE_URL.isEmpty()) {
|
||||||
|
System.out.println("PVE配置已设置");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("初始化检查时发生错误: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
src/main/java/top/gtb520/java/pve_back_api/config/AppConfig.java
Normal file
113
src/main/java/top/gtb520/java/pve_back_api/config/AppConfig.java
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package top.gtb520.java.pve_back_api.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局配置管理类
|
||||||
|
* 将配置文件中的配置项存储到全局静态变量中
|
||||||
|
*/
|
||||||
|
public class AppConfig {
|
||||||
|
|
||||||
|
// 全局配置
|
||||||
|
public static volatile String HTTP_IP = "0.0.0.0";
|
||||||
|
public static volatile String HTTP_PORT = "8080";
|
||||||
|
|
||||||
|
// 数据库配置
|
||||||
|
public static volatile String MYSQL_JDBC = "";
|
||||||
|
public static volatile String MYSQL_USERNAME = "";
|
||||||
|
public static volatile String MYSQL_PASSWORD = "";
|
||||||
|
|
||||||
|
// PVE配置 - 保留原有URL并新增拆解配置
|
||||||
|
public static volatile String PVE_URL = "";
|
||||||
|
public static volatile String PVE_HOSTNAME_IP = "";
|
||||||
|
public static volatile String PVE_PORT = "8006";
|
||||||
|
public static volatile String PVE_PROTOCOL = "https";
|
||||||
|
public static volatile String PVE_API_TOKEN_NAME = "";
|
||||||
|
public static volatile String PVE_API_TOKEN_ID = "";
|
||||||
|
public static volatile String PVE_API_TOKEN_SECRET = "";
|
||||||
|
|
||||||
|
// 配置状态
|
||||||
|
private static volatile boolean isLoaded = false;
|
||||||
|
|
||||||
|
private AppConfig() {
|
||||||
|
// 私有构造函数,防止实例化
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标记配置已加载
|
||||||
|
*/
|
||||||
|
public static void markAsLoaded() {
|
||||||
|
isLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查配置是否已加载
|
||||||
|
*/
|
||||||
|
public static boolean isConfigLoaded() {
|
||||||
|
return isLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取完整的PVE URL(优先使用拆解后的配置构建)
|
||||||
|
*/
|
||||||
|
public static String getPveFullUrl() {
|
||||||
|
// 如果有拆解后的配置,优先使用它们构建URL
|
||||||
|
if (PVE_HOSTNAME_IP != null && !PVE_HOSTNAME_IP.isEmpty()) {
|
||||||
|
return PVE_PROTOCOL + "://" + PVE_HOSTNAME_IP + ":" + PVE_PORT + "/";
|
||||||
|
}
|
||||||
|
// 否则返回原始URL
|
||||||
|
return PVE_URL != null ? PVE_URL : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取完整的数据库连接URL(包含用户名和密码)
|
||||||
|
*/
|
||||||
|
public static String getFullJdbcUrl() {
|
||||||
|
if (MYSQL_JDBC == null || MYSQL_JDBC.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return MYSQL_JDBC + "?user=" + MYSQL_USERNAME + "&password=" + MYSQL_PASSWORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证必要配置是否完整
|
||||||
|
*/
|
||||||
|
public static boolean validateRequiredConfig() {
|
||||||
|
return HTTP_IP != null && !HTTP_IP.isEmpty() &&
|
||||||
|
HTTP_PORT != null && !HTTP_PORT.isEmpty() &&
|
||||||
|
(PVE_URL != null && !PVE_URL.isEmpty()) ||
|
||||||
|
(PVE_HOSTNAME_IP != null && !PVE_HOSTNAME_IP.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印当前配置信息
|
||||||
|
*/
|
||||||
|
public static void printCurrentConfig() {
|
||||||
|
System.out.println("=== 应用配置信息 ===");
|
||||||
|
System.out.println("HTTP 监听地址: " + HTTP_IP + ":" + HTTP_PORT);
|
||||||
|
System.out.println("MySQL 连接: " + (MYSQL_JDBC != null ? "已配置" : "未配置"));
|
||||||
|
System.out.println("PVE 原始URL: " + (PVE_URL != null ? PVE_URL : "未配置"));
|
||||||
|
System.out.println("PVE 主机: " + (PVE_HOSTNAME_IP != null ? PVE_HOSTNAME_IP : "未配置"));
|
||||||
|
System.out.println("PVE 端口: " + PVE_PORT);
|
||||||
|
System.out.println("PVE 协议: " + PVE_PROTOCOL);
|
||||||
|
System.out.println("配置加载状态: " + (isLoaded ? "已完成" : "未完成"));
|
||||||
|
System.out.println("==================");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置配置到默认值
|
||||||
|
*/
|
||||||
|
public static void resetToDefaults() {
|
||||||
|
HTTP_IP = "0.0.0.0";
|
||||||
|
HTTP_PORT = "8080";
|
||||||
|
MYSQL_JDBC = "";
|
||||||
|
MYSQL_USERNAME = "";
|
||||||
|
MYSQL_PASSWORD = "";
|
||||||
|
PVE_URL = "";
|
||||||
|
PVE_HOSTNAME_IP = "";
|
||||||
|
PVE_PORT = "8006";
|
||||||
|
PVE_PROTOCOL = "https";
|
||||||
|
PVE_API_TOKEN_NAME = "";
|
||||||
|
PVE_API_TOKEN_ID = "";
|
||||||
|
PVE_API_TOKEN_SECRET = "";
|
||||||
|
isLoaded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,405 @@
|
|||||||
|
package top.gtb520.java.pve_back_api.config;
|
||||||
|
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置文件管理器
|
||||||
|
* 负责配置文件的检测、创建、加载和解析
|
||||||
|
*/
|
||||||
|
public class ConfigManager {
|
||||||
|
|
||||||
|
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||||
|
private static final String CONFIG_TEMPLATE_PATH = "config.yaml";
|
||||||
|
private static final String CONFIG_DIR_NAME = "conf";
|
||||||
|
private static final String CONFIG_FILE_NAME = "config.yaml";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化配置系统
|
||||||
|
* @return 配置文件路径
|
||||||
|
*/
|
||||||
|
public static String initializeConfig() {
|
||||||
|
lock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
String jarDir = getApplicationDirectory();
|
||||||
|
Path configDir = Paths.get(jarDir, CONFIG_DIR_NAME);
|
||||||
|
Path configFile = configDir.resolve(CONFIG_FILE_NAME);
|
||||||
|
|
||||||
|
System.out.println("应用运行目录: " + jarDir);
|
||||||
|
|
||||||
|
// 确保配置目录存在
|
||||||
|
createConfigDirectory(configDir);
|
||||||
|
|
||||||
|
// 检查并创建配置文件
|
||||||
|
if (!Files.exists(configFile)) {
|
||||||
|
createDefaultConfigFile(configFile);
|
||||||
|
} else {
|
||||||
|
System.out.println("配置文件已存在: " + configFile.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载配置到全局变量
|
||||||
|
loadConfiguration(configFile.toString());
|
||||||
|
|
||||||
|
return configFile.toString();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("配置初始化失败: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取应用运行目录
|
||||||
|
*/
|
||||||
|
private static String getApplicationDirectory() {
|
||||||
|
try {
|
||||||
|
String classPath = ConfigManager.class.getProtectionDomain()
|
||||||
|
.getCodeSource().getLocation().getPath();
|
||||||
|
classPath = java.net.URLDecoder.decode(classPath, "UTF-8");
|
||||||
|
|
||||||
|
File classFile = new File(classPath);
|
||||||
|
|
||||||
|
if (classFile.isFile() && classPath.endsWith(".jar")) {
|
||||||
|
return classFile.getParent();
|
||||||
|
} else if (classFile.isDirectory()) {
|
||||||
|
String projectDir = classFile.getAbsolutePath();
|
||||||
|
if (projectDir.contains("target" + File.separator + "classes")) {
|
||||||
|
return projectDir.substring(0, projectDir.indexOf("target"));
|
||||||
|
}
|
||||||
|
return projectDir;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("获取应用目录失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return System.getProperty("user.dir");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建配置目录
|
||||||
|
*/
|
||||||
|
private static void createConfigDirectory(Path configDir) throws IOException {
|
||||||
|
if (!Files.exists(configDir)) {
|
||||||
|
Files.createDirectories(configDir);
|
||||||
|
System.out.println("创建配置目录: " + configDir.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建默认配置文件
|
||||||
|
*/
|
||||||
|
private static void createDefaultConfigFile(Path configFile) throws IOException {
|
||||||
|
System.out.println("创建默认配置文件: " + configFile.toString());
|
||||||
|
|
||||||
|
try (InputStream templateStream = getResourceAsStream(CONFIG_TEMPLATE_PATH);
|
||||||
|
OutputStream outputStream = Files.newOutputStream(configFile)) {
|
||||||
|
|
||||||
|
if (templateStream == null) {
|
||||||
|
createMinimalConfigFile(configFile);
|
||||||
|
} else {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int length;
|
||||||
|
while ((length = templateStream.read(buffer)) > 0) {
|
||||||
|
outputStream.write(buffer, 0, length);
|
||||||
|
}
|
||||||
|
System.out.println("从模板创建配置文件成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建最小化配置文件(当模板不可用时)
|
||||||
|
*/
|
||||||
|
private static void createMinimalConfigFile(Path configFile) throws IOException {
|
||||||
|
String minimalConfig = "# PVE后端API配置文件\n" +
|
||||||
|
"global:\n" +
|
||||||
|
" http_ip: \"0.0.0.0\"\n" +
|
||||||
|
" http_port: \"8080\"\n\n" +
|
||||||
|
"database:\n" +
|
||||||
|
" mysql_jdbc: \"\"\n" +
|
||||||
|
" mysql_username: \"\"\n" +
|
||||||
|
" mysql_password: \"\"\n\n" +
|
||||||
|
"pve:\n" +
|
||||||
|
" # 推荐使用拆分配置(优先级更高)\n" +
|
||||||
|
" hostname_ip: \"10.168.2.18\"\n" +
|
||||||
|
" port: \"8006\"\n" +
|
||||||
|
" protocol: \"https\"\n" +
|
||||||
|
" # 或者使用完整URL(向后兼容)\n" +
|
||||||
|
" url: \"https://10.168.2.18:8006/\"\n" +
|
||||||
|
" # API Token配置\n" +
|
||||||
|
" api_tocken_name: \"dev\"\n" +
|
||||||
|
" api_token_id: \"root@pam!dev\"\n" +
|
||||||
|
" api_token_secret: \"68e9dac5-3110-4f1b-b33a-feeaa4014f63\"\n";
|
||||||
|
|
||||||
|
Files.write(configFile, minimalConfig.getBytes("UTF-8"));
|
||||||
|
System.out.println("创建最小化配置文件成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从资源获取输入流
|
||||||
|
*/
|
||||||
|
private static InputStream getResourceAsStream(String resourcePath) {
|
||||||
|
// 首先尝试从类路径获取
|
||||||
|
InputStream stream = ConfigManager.class.getClassLoader().getResourceAsStream(resourcePath);
|
||||||
|
if (stream != null) {
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果类路径中没有,尝试从文件系统获取
|
||||||
|
try {
|
||||||
|
Path resourceFile = Paths.get(resourcePath);
|
||||||
|
if (Files.exists(resourceFile)) {
|
||||||
|
return Files.newInputStream(resourceFile);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// 忽略文件系统访问异常
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载配置文件到全局变量
|
||||||
|
* @param configFilePath 配置文件路径
|
||||||
|
*/
|
||||||
|
private static void loadConfiguration(String configFilePath) {
|
||||||
|
try {
|
||||||
|
Map<String, Object> configData = loadYamlConfig(configFilePath);
|
||||||
|
|
||||||
|
if (configData == null) {
|
||||||
|
handleEmptyConfig();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析并设置配置值
|
||||||
|
parseAndSetConfig(configData);
|
||||||
|
AppConfig.markAsLoaded();
|
||||||
|
|
||||||
|
System.out.println("✅ 配置加载成功!");
|
||||||
|
AppConfig.printCurrentConfig();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
handleConfigLoadError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载YAML配置文件
|
||||||
|
*/
|
||||||
|
private static Map<String, Object> loadYamlConfig(String configFilePath) throws IOException {
|
||||||
|
Yaml yaml = new Yaml();
|
||||||
|
try (InputStream inputStream = Files.newInputStream(Paths.get(configFilePath))) {
|
||||||
|
return yaml.load(inputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理空配置情况
|
||||||
|
*/
|
||||||
|
private static void handleEmptyConfig() {
|
||||||
|
System.err.println("❌ 配置文件为空或格式错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理配置加载错误
|
||||||
|
*/
|
||||||
|
private static void handleConfigLoadError(Exception e) {
|
||||||
|
System.err.println("❌ 加载配置文件失败: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析配置数据并设置到全局变量
|
||||||
|
* @param data 配置数据映射
|
||||||
|
*/
|
||||||
|
private static void parseAndSetConfig(Map<String, Object> data) {
|
||||||
|
try {
|
||||||
|
parseGlobalSection(data);
|
||||||
|
parseDatabaseSection(data);
|
||||||
|
parsePveSection(data);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("❌ 解析配置数据时发生错误: " + e.getMessage());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析全局配置部分
|
||||||
|
*/
|
||||||
|
private static void parseGlobalSection(Map<String, Object> data) {
|
||||||
|
if (!data.containsKey("global")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> global = (Map<String, Object>) data.get("global");
|
||||||
|
AppConfig.HTTP_IP = getStringValue(global, "http_ip", AppConfig.HTTP_IP);
|
||||||
|
AppConfig.HTTP_PORT = getStringValue(global, "http_port", AppConfig.HTTP_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析数据库配置部分
|
||||||
|
*/
|
||||||
|
private static void parseDatabaseSection(Map<String, Object> data) {
|
||||||
|
if (!data.containsKey("database")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> database = (Map<String, Object>) data.get("database");
|
||||||
|
AppConfig.MYSQL_JDBC = getStringValue(database, "mysql_jdbc", AppConfig.MYSQL_JDBC);
|
||||||
|
AppConfig.MYSQL_USERNAME = getStringValue(database, "mysql_username", AppConfig.MYSQL_USERNAME);
|
||||||
|
AppConfig.MYSQL_PASSWORD = getStringValue(database, "mysql_password", AppConfig.MYSQL_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析PVE配置部分
|
||||||
|
*/
|
||||||
|
private static void parsePveSection(Map<String, Object> data) {
|
||||||
|
if (!data.containsKey("pve")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> pve = (Map<String, Object>) data.get("pve");
|
||||||
|
processPveUrlConfig(pve);
|
||||||
|
processPveApiTokens(pve);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理PVE URL相关配置
|
||||||
|
*/
|
||||||
|
private static void processPveUrlConfig(Map<String, Object> pve) {
|
||||||
|
// 保留原始URL配置
|
||||||
|
AppConfig.PVE_URL = getStringValue(pve, "url", AppConfig.PVE_URL);
|
||||||
|
|
||||||
|
// 解析拆分后的PVE配置(优先级更高)
|
||||||
|
AppConfig.PVE_HOSTNAME_IP = getStringValue(pve, "hostname_ip", AppConfig.PVE_HOSTNAME_IP);
|
||||||
|
AppConfig.PVE_PORT = getStringValue(pve, "port", AppConfig.PVE_PORT);
|
||||||
|
AppConfig.PVE_PROTOCOL = getStringValue(pve, "protocol", AppConfig.PVE_PROTOCOL);
|
||||||
|
|
||||||
|
// 如果提供了拆分配置,同时更新URL
|
||||||
|
if (hasValidHostname()) {
|
||||||
|
AppConfig.PVE_URL = buildPveUrl();
|
||||||
|
}
|
||||||
|
// 如果只有URL而没有拆分配置,则解析URL
|
||||||
|
else if (hasValidUrl()) {
|
||||||
|
parsePveUrl(AppConfig.PVE_URL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理PVE API令牌配置
|
||||||
|
*/
|
||||||
|
private static void processPveApiTokens(Map<String, Object> pve) {
|
||||||
|
AppConfig.PVE_API_TOKEN_NAME = getStringValue(pve, "api_tocken_name", AppConfig.PVE_API_TOKEN_NAME);
|
||||||
|
AppConfig.PVE_API_TOKEN_ID = getStringValue(pve, "api_token_id", AppConfig.PVE_API_TOKEN_ID);
|
||||||
|
AppConfig.PVE_API_TOKEN_SECRET = getStringValue(pve, "api_token_secret", AppConfig.PVE_API_TOKEN_SECRET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有有效的主机名配置
|
||||||
|
*/
|
||||||
|
private static boolean hasValidHostname() {
|
||||||
|
return AppConfig.PVE_HOSTNAME_IP != null && !AppConfig.PVE_HOSTNAME_IP.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有有效的URL配置
|
||||||
|
*/
|
||||||
|
private static boolean hasValidUrl() {
|
||||||
|
return AppConfig.PVE_URL != null && !AppConfig.PVE_URL.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建PVE完整URL
|
||||||
|
*/
|
||||||
|
private static String buildPveUrl() {
|
||||||
|
return AppConfig.PVE_PROTOCOL + "://" + AppConfig.PVE_HOSTNAME_IP + ":" + AppConfig.PVE_PORT + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从完整URL解析PVE主机和端口信息
|
||||||
|
*/
|
||||||
|
private static void parsePveUrl(String url) {
|
||||||
|
try {
|
||||||
|
if (url == null || url.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除末尾的斜杠
|
||||||
|
if (url.endsWith("/")) {
|
||||||
|
url = url.substring(0, url.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析协议
|
||||||
|
if (url.startsWith("https://")) {
|
||||||
|
AppConfig.PVE_PROTOCOL = "https";
|
||||||
|
url = url.substring(8);
|
||||||
|
} else if (url.startsWith("http://")) {
|
||||||
|
AppConfig.PVE_PROTOCOL = "http";
|
||||||
|
url = url.substring(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析主机和端口
|
||||||
|
int portIndex = url.lastIndexOf(":");
|
||||||
|
if (portIndex > 0) {
|
||||||
|
AppConfig.PVE_HOSTNAME_IP = url.substring(0, portIndex);
|
||||||
|
String portStr = url.substring(portIndex + 1);
|
||||||
|
if (!portStr.isEmpty()) {
|
||||||
|
AppConfig.PVE_PORT = portStr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AppConfig.PVE_HOSTNAME_IP = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("解析PVE URL时发生错误: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全获取字符串值
|
||||||
|
*/
|
||||||
|
private static String getStringValue(Map<String, Object> map, String key, String defaultValue) {
|
||||||
|
if (map.containsKey(key)) {
|
||||||
|
Object value = map.get(key);
|
||||||
|
return value != null ? value.toString() : defaultValue;
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新加载配置
|
||||||
|
*/
|
||||||
|
public static boolean reloadConfiguration() {
|
||||||
|
lock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
String jarDir = getApplicationDirectory();
|
||||||
|
Path configFile = Paths.get(jarDir, CONFIG_DIR_NAME, CONFIG_FILE_NAME);
|
||||||
|
|
||||||
|
if (Files.exists(configFile)) {
|
||||||
|
AppConfig.resetToDefaults();
|
||||||
|
loadConfiguration(configFile.toString());
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
System.err.println("配置文件不存在,无法重新加载");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("重新加载配置失败: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package top.gtb520.java.pve_back_api.config;
|
||||||
|
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局配置类
|
||||||
|
* 存储所有配置项的全局变量
|
||||||
|
*/
|
||||||
|
public class GlobalConfig {
|
||||||
|
|
||||||
|
// 全局配置
|
||||||
|
public static String HTTP_IP = "0.0.0.0";
|
||||||
|
public static String HTTP_PORT = "8080";
|
||||||
|
|
||||||
|
// 数据库配置
|
||||||
|
public static String MYSQL_JDBC = "";
|
||||||
|
public static String MYSQL_USERNAME = "";
|
||||||
|
public static String MYSQL_PASSWORD = "";
|
||||||
|
|
||||||
|
// PVE配置
|
||||||
|
public static String PVE_URL = "";
|
||||||
|
public static String PVE_API_TOKEN_NAME = "";
|
||||||
|
public static String PVE_API_TOKEN_ID = "";
|
||||||
|
public static String PVE_API_TOKEN_SECRET = "";
|
||||||
|
|
||||||
|
// 私有构造函数,防止实例化
|
||||||
|
private GlobalConfig() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印所有配置信息(用于调试)
|
||||||
|
*/
|
||||||
|
public static void printConfig() {
|
||||||
|
System.out.println("=== 当前配置信息 ===");
|
||||||
|
System.out.println("HTTP IP: " + HTTP_IP);
|
||||||
|
System.out.println("HTTP PORT: " + HTTP_PORT);
|
||||||
|
System.out.println("MYSQL JDBC: " + MYSQL_JDBC);
|
||||||
|
System.out.println("MYSQL USERNAME: " + MYSQL_USERNAME);
|
||||||
|
System.out.println("PVE URL: " + PVE_URL);
|
||||||
|
System.out.println("PVE API TOKEN NAME: " + PVE_API_TOKEN_NAME);
|
||||||
|
System.out.println("PVE API TOKEN ID: " + PVE_API_TOKEN_ID);
|
||||||
|
System.out.println("===================");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
package top.gtb520.java.pve_back_api.config;
|
||||||
|
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* YAML配置文件解析器
|
||||||
|
* 负责从不同来源加载和解析YAML配置文件
|
||||||
|
*/
|
||||||
|
public class YamlConfigLoader {
|
||||||
|
|
||||||
|
private static final String GLOBAL_SECTION = "global";
|
||||||
|
private static final String DATABASE_SECTION = "database";
|
||||||
|
private static final String PVE_SECTION = "pve";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从资源文件加载配置
|
||||||
|
* @param resourcePath 资源文件路径
|
||||||
|
*/
|
||||||
|
public static void loadConfigFromResource(String resourcePath) {
|
||||||
|
try {
|
||||||
|
Yaml yaml = new Yaml();
|
||||||
|
InputStream inputStream = YamlConfigLoader.class.getClassLoader().getResourceAsStream(resourcePath);
|
||||||
|
|
||||||
|
if (inputStream == null) {
|
||||||
|
handleResourceNotFound(resourcePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> data = yaml.load(inputStream);
|
||||||
|
parseConfig(data);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
handleLoadException("资源", resourcePath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从文件路径加载配置
|
||||||
|
* @param filePath 文件路径
|
||||||
|
*/
|
||||||
|
public static void loadConfigFromFile(String filePath) {
|
||||||
|
try {
|
||||||
|
Yaml yaml = new Yaml();
|
||||||
|
InputStream inputStream = java.nio.file.Files.newInputStream(java.nio.file.Paths.get(filePath));
|
||||||
|
|
||||||
|
Map<String, Object> data = yaml.load(inputStream);
|
||||||
|
parseConfig(data);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
handleLoadException("文件", filePath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析配置数据并设置到全局变量
|
||||||
|
* @param data 配置数据映射
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static void parseConfig(Map<String, Object> data) {
|
||||||
|
try {
|
||||||
|
parseGlobalConfig(data);
|
||||||
|
parseDatabaseConfig(data);
|
||||||
|
parsePveConfig(data);
|
||||||
|
|
||||||
|
System.out.println("配置加载成功!");
|
||||||
|
GlobalConfig.printConfig();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("解析配置数据时发生错误: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析全局配置部分
|
||||||
|
*/
|
||||||
|
private static void parseGlobalConfig(Map<String, Object> data) {
|
||||||
|
if (!data.containsKey(GLOBAL_SECTION)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> global = (Map<String, Object>) data.get(GLOBAL_SECTION);
|
||||||
|
GlobalConfig.HTTP_IP = getStringValue(global, "http_ip", GlobalConfig.HTTP_IP);
|
||||||
|
GlobalConfig.HTTP_PORT = getStringValue(global, "http_port", GlobalConfig.HTTP_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析数据库配置部分
|
||||||
|
*/
|
||||||
|
private static void parseDatabaseConfig(Map<String, Object> data) {
|
||||||
|
if (!data.containsKey(DATABASE_SECTION)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> database = (Map<String, Object>) data.get(DATABASE_SECTION);
|
||||||
|
GlobalConfig.MYSQL_JDBC = getStringValue(database, "mysql_jdbc", GlobalConfig.MYSQL_JDBC);
|
||||||
|
GlobalConfig.MYSQL_USERNAME = getStringValue(database, "mysql_username", GlobalConfig.MYSQL_USERNAME);
|
||||||
|
GlobalConfig.MYSQL_PASSWORD = getStringValue(database, "mysql_password", GlobalConfig.MYSQL_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析PVE配置部分
|
||||||
|
*/
|
||||||
|
private static void parsePveConfig(Map<String, Object> data) {
|
||||||
|
if (!data.containsKey(PVE_SECTION)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> pve = (Map<String, Object>) data.get(PVE_SECTION);
|
||||||
|
GlobalConfig.PVE_URL = getStringValue(pve, "url", GlobalConfig.PVE_URL);
|
||||||
|
GlobalConfig.PVE_API_TOKEN_NAME = getStringValue(pve, "api_tocken_name", GlobalConfig.PVE_API_TOKEN_NAME);
|
||||||
|
GlobalConfig.PVE_API_TOKEN_ID = getStringValue(pve, "api_token_id", GlobalConfig.PVE_API_TOKEN_ID);
|
||||||
|
GlobalConfig.PVE_API_TOKEN_SECRET = getStringValue(pve, "api_token_secret", GlobalConfig.PVE_API_TOKEN_SECRET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全获取字符串值
|
||||||
|
*/
|
||||||
|
private static String getStringValue(Map<String, Object> map, String key, String defaultValue) {
|
||||||
|
if (map.containsKey(key)) {
|
||||||
|
Object value = map.get(key);
|
||||||
|
return value != null ? value.toString() : defaultValue;
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理资源未找到的情况
|
||||||
|
*/
|
||||||
|
private static void handleResourceNotFound(String resourcePath) {
|
||||||
|
System.err.println("❌ 无法找到资源配置文件: " + resourcePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理加载异常
|
||||||
|
*/
|
||||||
|
private static void handleLoadException(String type, String path, Exception e) {
|
||||||
|
System.err.println("❌ 解析" + type + "配置文件失败: " + path);
|
||||||
|
System.err.println("错误详情: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
168
src/main/java/top/gtb520/java/pve_back_api/route/pve/status.java
Normal file
168
src/main/java/top/gtb520/java/pve_back_api/route/pve/status.java
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
package top.gtb520.java.pve_back_api.route.pve;
|
||||||
|
|
||||||
|
import it.corsinvest.proxmoxve.api.*;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
// 导入配置变量
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static top.gtb520.java.pve_back_api.config.AppConfig.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PVE状态相关路由处理器
|
||||||
|
* 提供与Proxmox VE服务器通信的客户端创建功能和状态查询
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
public class status {
|
||||||
|
|
||||||
|
private static final int DEFAULT_TIMEOUT = 120000; // 2分钟超时时间
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API:/api/pve/status
|
||||||
|
* 类型:GET
|
||||||
|
* 获取PVE集群状态信息,包括节点、虚拟机、容器等资源状态
|
||||||
|
*
|
||||||
|
* @return 包含集群状态信息的列表
|
||||||
|
* @throws Exception 当获取状态失败时抛出
|
||||||
|
*/
|
||||||
|
@GetMapping("/api/pve/status")
|
||||||
|
public List<Map<String, Object>> GetPveStatus() throws Exception {
|
||||||
|
System.out.println("开始获取PVE状态信息...");
|
||||||
|
List<Map<String, Object>> pveStatus = new ArrayList<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 创建PVE客户端
|
||||||
|
PveClient client = createClient();
|
||||||
|
|
||||||
|
// 获取集群资源信息
|
||||||
|
var resourcesResult = client.getCluster().getResources().resources();
|
||||||
|
|
||||||
|
if (resourcesResult.isSuccessStatusCode()) {
|
||||||
|
JsonNode resources = resourcesResult.getResponse().get("data");
|
||||||
|
|
||||||
|
// 统计各类资源
|
||||||
|
int nodeCount = 0, vmCount = 0, ctCount = 0;
|
||||||
|
int runningNodes = 0, runningVms = 0, runningCts = 0;
|
||||||
|
|
||||||
|
// 处理每个资源
|
||||||
|
for (JsonNode resource : resources) {
|
||||||
|
String type = resource.get("type").asText();
|
||||||
|
String status = resource.get("status").asText();
|
||||||
|
|
||||||
|
Map<String, Object> resourceInfo = new HashMap<>();
|
||||||
|
resourceInfo.put("type", type);
|
||||||
|
resourceInfo.put("status", status);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "node":
|
||||||
|
nodeCount++;
|
||||||
|
if ("online".equals(status)) runningNodes++;
|
||||||
|
resourceInfo.put("name", resource.get("node").asText());
|
||||||
|
resourceInfo.put("cpu_usage", String.format("%.2f%%",
|
||||||
|
resource.get("cpu").asDouble() * 100));
|
||||||
|
resourceInfo.put("memory_usage", String.format("%.2f%%",
|
||||||
|
(resource.get("mem").asDouble() / resource.get("maxmem").asDouble()) * 100));
|
||||||
|
resourceInfo.put("uptime", formatUptime(resource.get("uptime").asInt()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "qemu":
|
||||||
|
vmCount++;
|
||||||
|
if ("running".equals(status)) runningVms++;
|
||||||
|
resourceInfo.put("vmid", resource.get("vmid").asInt());
|
||||||
|
resourceInfo.put("name", resource.get("name").asText());
|
||||||
|
resourceInfo.put("node", resource.get("node").asText());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "lxc":
|
||||||
|
ctCount++;
|
||||||
|
if ("running".equals(status)) runningCts++;
|
||||||
|
resourceInfo.put("vmid", resource.get("vmid").asInt());
|
||||||
|
resourceInfo.put("name", resource.get("name").asText());
|
||||||
|
resourceInfo.put("node", resource.get("node").asText());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pveStatus.add(resourceInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加统计信息
|
||||||
|
Map<String, Object> summary = new HashMap<>();
|
||||||
|
summary.put("summary", true);
|
||||||
|
summary.put("total_nodes", nodeCount);
|
||||||
|
summary.put("running_nodes", runningNodes);
|
||||||
|
summary.put("total_vms", vmCount);
|
||||||
|
summary.put("running_vms", runningVms);
|
||||||
|
summary.put("total_containers", ctCount);
|
||||||
|
summary.put("running_containers", runningCts);
|
||||||
|
summary.put("timestamp", System.currentTimeMillis());
|
||||||
|
pveStatus.add(summary);
|
||||||
|
|
||||||
|
System.out.println("✅ 成功获取PVE状态信息");
|
||||||
|
System.out.printf("📊 统计: 节点%d(%d运行), VM%d(%d运行), 容器%d(%d运行)%n",
|
||||||
|
nodeCount, runningNodes, vmCount, runningVms, ctCount, runningCts);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 更安全的错误处理
|
||||||
|
String errorMsg = "未知错误";
|
||||||
|
try {
|
||||||
|
if (resourcesResult != null) {
|
||||||
|
errorMsg = resourcesResult.getError();
|
||||||
|
} else {
|
||||||
|
errorMsg = "API调用返回null结果";
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
errorMsg = "错误处理异常: " + e.getClass().getSimpleName() + " - " + e.getMessage();
|
||||||
|
}
|
||||||
|
throw new Exception("获取集群资源失败: " + errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("❌ 获取PVE状态失败: " + e.getMessage());
|
||||||
|
throw new Exception("获取PVE状态失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pveStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化运行时间
|
||||||
|
*/
|
||||||
|
private static String formatUptime(int seconds) {
|
||||||
|
int days = seconds / 86400;
|
||||||
|
int hours = (seconds % 86400) / 3600;
|
||||||
|
int minutes = (seconds % 3600) / 60;
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (days > 0) sb.append(days).append("天 ");
|
||||||
|
if (hours > 0) sb.append(hours).append("小时 ");
|
||||||
|
if (minutes > 0) sb.append(minutes).append("分钟");
|
||||||
|
|
||||||
|
return sb.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建PVE客户端实例
|
||||||
|
* 支持API令牌认证和传统登录认证两种方式
|
||||||
|
*
|
||||||
|
* @return 配置好的PveClient实例
|
||||||
|
* @throws RuntimeException 当客户端创建失败时抛出
|
||||||
|
*/
|
||||||
|
public static PveClient createClient() throws RuntimeException {
|
||||||
|
// 构建API_TOKEN字符串
|
||||||
|
String[] api_token_list = PVE_API_TOKEN_ID.split("!");
|
||||||
|
String api_token = api_token_list[0] + ":" + api_token_list[1] + "@" + "token=" + PVE_API_TOKEN_SECRET;
|
||||||
|
System.out.println("API_TOKEN: " + api_token);
|
||||||
|
|
||||||
|
// 创建PVE客户端实例
|
||||||
|
var client = new PveClient(PVE_HOSTNAME_IP, Integer.parseInt(PVE_PORT));
|
||||||
|
client.setValidateCertificate(false); // SSL证书验证
|
||||||
|
client.setTimeout(DEFAULT_TIMEOUT); // 设置请求超时时间
|
||||||
|
client.setApiToken(api_token);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/main/java/top/gtb520/java/pve_back_api/route/test.java
Normal file
12
src/main/java/top/gtb520/java/pve_back_api/route/test.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package top.gtb520.java.pve_back_api.route;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class test {
|
||||||
|
@GetMapping("/test")
|
||||||
|
public String test() {
|
||||||
|
return "test";
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/main/resources/application.properties
Normal file
1
src/main/resources/application.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
spring.application.name=pve-back-api
|
||||||
18
src/main/resources/config.yaml
Normal file
18
src/main/resources/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# 全局配置
|
||||||
|
global:
|
||||||
|
http_ip: "0.0.0.0"
|
||||||
|
http_port: "8080"
|
||||||
|
|
||||||
|
# 数据库配置
|
||||||
|
database:
|
||||||
|
mysql_jdbc: "jdbc:mysql://10.168.2.2:3306/pve_monitor?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalInfile=true&allowUrlInLocalInfile=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&allowPublicKeyRetrieval=true&allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&allowPublicKeyRetrieval=true&allowLoadLocalInfile=true&allowUrlInLocal"
|
||||||
|
mysql_username: "root"
|
||||||
|
mysql_password: "Password"
|
||||||
|
|
||||||
|
# PVE配置
|
||||||
|
pve:
|
||||||
|
# PVE后端地址,示例:https://192.168.1.1:8006/ (协议HTTPS,带结尾的“/”,不保留结尾其他参数)
|
||||||
|
url: "https://10.168.2.18:8006/"
|
||||||
|
api_tocken_name: "dev"
|
||||||
|
api_token_id: "root@pam!dev"
|
||||||
|
api_token_secret: "68e9dac5-3110-4f1b-b33a-feeaa4014f63"
|
||||||
56
src/test/java/top/gtb520/java/pve_back_api/MainTests.java
Normal file
56
src/test/java/top/gtb520/java/pve_back_api/MainTests.java
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package top.gtb520.java.pve_back_api;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import top.gtb520.java.pve_back_api.config.AppConfig;
|
||||||
|
import top.gtb520.java.pve_back_api.config.ConfigManager;
|
||||||
|
import top.gtb520.java.pve_back_api.config.YamlConfigLoader;
|
||||||
|
import top.gtb520.java.pve_back_api.route.pve.status;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class MainTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAppConfigInitialization() {
|
||||||
|
// 测试配置类的基本功能
|
||||||
|
assertNotNull(AppConfig.HTTP_IP);
|
||||||
|
assertNotNull(AppConfig.HTTP_PORT);
|
||||||
|
assertFalse(AppConfig.isConfigLoaded());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testConfigManagerMethodsExist() {
|
||||||
|
// 测试ConfigManager类的方法是否存在
|
||||||
|
assertTrue(ConfigManager.class.getDeclaredMethods().length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testYamlConfigLoaderMethodsExist() {
|
||||||
|
// 测试YamlConfigLoader类的方法是否存在
|
||||||
|
assertTrue(YamlConfigLoader.class.getDeclaredMethods().length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testStatusClassExists() {
|
||||||
|
// 测试status类是否存在createClient方法
|
||||||
|
assertNotNull(status.class.getDeclaredMethods());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPveUrlBuilding() {
|
||||||
|
// 测试PVE URL构建逻辑
|
||||||
|
AppConfig.PVE_PROTOCOL = "https";
|
||||||
|
AppConfig.PVE_HOSTNAME_IP = "192.168.1.100";
|
||||||
|
AppConfig.PVE_PORT = "8006";
|
||||||
|
|
||||||
|
String expectedUrl = "https://192.168.1.100:8006/";
|
||||||
|
assertEquals(expectedUrl, AppConfig.getPveFullUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package top.gtb520.java.pve_back_api;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||||
|
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||||
|
import reactor.netty.http.client.HttpClient;
|
||||||
|
import reactor.netty.tcp.TcpClient;
|
||||||
|
|
||||||
|
public class ResourcesResultTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建忽略SSL证书验证的WebClient实例
|
||||||
|
* 用于测试环境或自签名证书场景
|
||||||
|
*/
|
||||||
|
private static WebClient createInsecureWebClient() {
|
||||||
|
try {
|
||||||
|
// 使用Netty内置的不安全信任管理器工厂
|
||||||
|
TcpClient tcpClient = TcpClient.create()
|
||||||
|
.secure(sslContextSpec -> {
|
||||||
|
try {
|
||||||
|
sslContextSpec.sslContext(
|
||||||
|
io.netty.handler.ssl.SslContextBuilder.forClient()
|
||||||
|
.trustManager(io.netty.handler.ssl.util.InsecureTrustManagerFactory.INSTANCE)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("SSL配置失败", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建HttpClient和连接器
|
||||||
|
HttpClient httpClient = HttpClient.from(tcpClient);
|
||||||
|
ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
|
||||||
|
|
||||||
|
// 构建并返回WebClient
|
||||||
|
return WebClient.builder()
|
||||||
|
.clientConnector(connector)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("创建WebClient失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testHttpsRequest() {
|
||||||
|
System.out.println("开始发送HTTPS请求...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 创建忽略证书验证的WebClient
|
||||||
|
WebClient client = createInsecureWebClient();
|
||||||
|
|
||||||
|
// 发送HTTPS GET请求
|
||||||
|
Mono<String> result = client.get()
|
||||||
|
.uri("https://10.168.2.2:8013/")
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(String.class);
|
||||||
|
|
||||||
|
// 阻塞获取响应结果
|
||||||
|
String response = result.block();
|
||||||
|
System.out.println("✅ 请求成功!");
|
||||||
|
System.out.println("响应内容长度: " + (response != null ? response.length() : 0) + " 字符");
|
||||||
|
|
||||||
|
// 如果需要查看完整响应内容,取消下面的注释
|
||||||
|
// System.out.println("响应内容: " + response);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("❌ HTTPS请求失败: " + e.getMessage());
|
||||||
|
System.err.println("可能的原因:");
|
||||||
|
System.err.println("1. 目标服务器不可达");
|
||||||
|
System.err.println("2. 端口未开放");
|
||||||
|
System.err.println("3. 网络连接问题");
|
||||||
|
System.err.println("4. SSL配置问题");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
target/classes/application.properties
Normal file
1
target/classes/application.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
spring.application.name=pve-back-api
|
||||||
18
target/classes/config.yaml
Normal file
18
target/classes/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# 全局配置
|
||||||
|
global:
|
||||||
|
http_ip: "0.0.0.0"
|
||||||
|
http_port: "8080"
|
||||||
|
|
||||||
|
# 数据库配置
|
||||||
|
database:
|
||||||
|
mysql_jdbc: "jdbc:mysql://10.168.2.2:3306/pve_monitor?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalInfile=true&allowUrlInLocalInfile=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&allowPublicKeyRetrieval=true&allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&allowPublicKeyRetrieval=true&allowLoadLocalInfile=true&allowUrlInLocal"
|
||||||
|
mysql_username: "root"
|
||||||
|
mysql_password: "Password"
|
||||||
|
|
||||||
|
# PVE配置
|
||||||
|
pve:
|
||||||
|
# PVE后端地址,示例:https://192.168.1.1:8006/ (协议HTTPS,带结尾的“/”,不保留结尾其他参数)
|
||||||
|
url: "https://10.168.2.18:8006/"
|
||||||
|
api_tocken_name: "dev"
|
||||||
|
api_token_id: "root@pam!dev"
|
||||||
|
api_token_secret: "68e9dac5-3110-4f1b-b33a-feeaa4014f63"
|
||||||
BIN
target/classes/top/gtb520/java/pve_back_api/Main.class
Normal file
BIN
target/classes/top/gtb520/java/pve_back_api/Main.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/top/gtb520/java/pve_back_api/route/test.class
Normal file
BIN
target/classes/top/gtb520/java/pve_back_api/route/test.class
Normal file
Binary file not shown.
@@ -0,0 +1,7 @@
|
|||||||
|
top\gtb520\java\pve_back_api\route\pve\status.class
|
||||||
|
top\gtb520\java\pve_back_api\config\ConfigManager.class
|
||||||
|
top\gtb520\java\pve_back_api\config\GlobalConfig.class
|
||||||
|
top\gtb520\java\pve_back_api\Main.class
|
||||||
|
top\gtb520\java\pve_back_api\config\AppConfig.class
|
||||||
|
top\gtb520\java\pve_back_api\config\YamlConfigLoader.class
|
||||||
|
top\gtb520\java\pve_back_api\route\test.class
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
G:\Project\Java\pve-back-api\src\main\java\top\gtb520\java\pve_back_api\config\AppConfig.java
|
||||||
|
G:\Project\Java\pve-back-api\src\main\java\top\gtb520\java\pve_back_api\config\ConfigManager.java
|
||||||
|
G:\Project\Java\pve-back-api\src\main\java\top\gtb520\java\pve_back_api\config\GlobalConfig.java
|
||||||
|
G:\Project\Java\pve-back-api\src\main\java\top\gtb520\java\pve_back_api\config\YamlConfigLoader.java
|
||||||
|
G:\Project\Java\pve-back-api\src\main\java\top\gtb520\java\pve_back_api\Main.java
|
||||||
|
G:\Project\Java\pve-back-api\src\main\java\top\gtb520\java\pve_back_api\route\pve\status.java
|
||||||
|
G:\Project\Java\pve-back-api\src\main\java\top\gtb520\java\pve_back_api\route\test.java
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
top\gtb520\java\pve_back_api\MainTests.class
|
||||||
|
top\gtb520\java\pve_back_api\ResourcesResultTest.class
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
G:\Project\Java\pve-back-api\src\test\java\top\gtb520\java\pve_back_api\MainTests.java
|
||||||
|
G:\Project\Java\pve-back-api\src\test\java\top\gtb520\java\pve_back_api\ResourcesResultTest.java
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Created at 2026-02-04T18:14:51.799
|
||||||
|
Boot Manifest-JAR contains absolute paths in classpath 'G:\Project\Java\pve-back-api\target\test-classes'
|
||||||
|
Hint: <argLine>-Djdk.net.URLClassPath.disableClassPathURLCheck=true</argLine>
|
||||||
|
'other' has different root
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
|||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Test set: top.gtb520.java.pve_back_api.ResourcesResultTest
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.140 s -- in top.gtb520.java.pve_back_api.ResourcesResultTest
|
||||||
BIN
target/test-classes/top/gtb520/java/pve_back_api/MainTests.class
Normal file
BIN
target/test-classes/top/gtb520/java/pve_back_api/MainTests.class
Normal file
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user