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