Compare commits
131 Commits
cabab2099d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
1bc3057819
|
|||
|
5efe14c78a
|
|||
|
84fbf6debc
|
|||
|
|
a519d68249 | ||
|
|
672abcbf07 | ||
|
|
da4082986c | ||
|
|
c7c9173058 | ||
|
|
733684ac1b | ||
|
|
d9c365060b | ||
|
|
e1d65e125e | ||
|
|
a8358e5c0f | ||
|
|
f688820d83 | ||
|
|
848b4eedbd | ||
|
|
5b0e285d0a | ||
|
|
361960cff4 | ||
|
|
27dc093053 | ||
|
|
050b08f76d | ||
|
|
d34364a2c3 | ||
|
|
5fb1e9517a | ||
|
|
0455f56480 | ||
|
|
1d04a91a22 | ||
|
|
6e65ddc6c2 | ||
|
|
7e77f1b503 | ||
|
|
32bec8515c | ||
|
|
717a7b114a | ||
|
|
ba070bff43 | ||
|
|
bd83350dbe | ||
|
|
9f822e45ef | ||
|
|
299b1f961a | ||
|
|
a53e7141d6 | ||
|
|
46587625a2 | ||
|
|
babbecd78d | ||
|
|
5033b72100 | ||
|
|
dfbe80494e | ||
|
|
4d62162f39 | ||
|
|
0770c68830 | ||
|
|
544beae49e | ||
|
|
7e6564ede1 | ||
|
|
342e08b044 | ||
|
|
660f22932e | ||
|
|
911edf34b9 | ||
|
|
4332a7158e | ||
|
|
2b2102041e | ||
|
abb35e94c0
|
|||
|
|
48e8f38ba8 | ||
|
|
e8e4808586 | ||
|
|
9b77876fa5 | ||
|
|
fc0290c76f | ||
|
|
c81b8892d8 | ||
|
|
77340b6c4f | ||
|
|
08cef32268 | ||
|
|
16746066b7 | ||
|
|
cc19498098 | ||
|
|
a75413b232 | ||
|
|
e601cb684b | ||
|
|
9f81701eda | ||
|
|
1485176f6b | ||
|
|
f203b319eb | ||
|
|
b4a183aaa2 | ||
|
|
60721f593c | ||
|
|
c2fb8adac0 | ||
|
|
a2e7980eec | ||
|
d404e84a4f
|
|||
|
654b5d807b
|
|||
|
e15886c457
|
|||
|
cb8640ebd3
|
|||
|
5b5494108c
|
|||
|
|
fa973a8204 | ||
|
|
a4a87d51f0 | ||
|
|
c93a049331 | ||
|
|
5563625a62 | ||
|
|
b875c895ab | ||
|
|
604a931b8c | ||
|
|
a448ae9452 | ||
|
|
d3683789d5 | ||
|
|
6c11649db9 | ||
|
|
9e54f9223b | ||
|
|
ba3a0e4857 | ||
|
|
395fe8b8b4 | ||
|
|
29475f0985 | ||
|
|
ca6a3e8e65 | ||
|
|
cf6cdf5a46 | ||
|
|
c94ba98f43 | ||
|
|
9468adbfab | ||
|
|
bae2b782ac | ||
|
|
07ada68c79 | ||
|
|
8ae5b0015d | ||
|
|
1b3ed6cf78 | ||
|
|
2736d7efb9 | ||
|
|
ebe972e6ae | ||
|
|
f3d0f056e6 | ||
|
|
61f6762ff8 | ||
|
|
86b9ad6e1e | ||
|
|
9aec4b8131 | ||
|
|
bea4253bc8 | ||
|
|
b6749d7103 | ||
|
|
b2ea87d0b8 | ||
|
|
b5afafba3e | ||
|
|
9b75827f35 | ||
|
|
fec3fb4f8b | ||
|
3de92f48d9
|
|||
|
|
a9a52132a4 | ||
|
|
a1180be597 | ||
|
|
4713c2736d | ||
|
|
298497be0c | ||
|
|
e5eebe8d38 | ||
|
|
bc0278687d | ||
|
|
cb55113524 | ||
|
|
b2ec9c414b | ||
|
|
3a7ba80cbe | ||
|
|
cd488ebd3e | ||
|
|
a97fc7c2aa | ||
|
|
df983daba8 | ||
|
|
462fe06dc2 | ||
|
|
44821a9247 | ||
|
|
2c055b1819 | ||
|
5e1ce9a0fa
|
|||
|
daa3e534ee
|
|||
|
0060fc2e6f
|
|||
|
2fae41338c
|
|||
|
d569b7f084
|
|||
|
7926dcd6d6
|
|||
|
fd8942b3b7
|
|||
|
9b7a1159f5
|
|||
|
92a0d2aafb
|
|||
|
c38b8767a5
|
|||
| fd12704bd5 | |||
|
048c11ee62
|
|||
|
4910bd9122
|
|||
|
83bc97bb02
|
|||
|
f6c0a31fc1
|
7
.dockerignore
Normal file
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
||||
.idea/
|
||||
.gradle/
|
||||
k8s/
|
||||
build/
|
||||
.github/
|
||||
uploads/
|
||||
apps/
|
||||
37
.drone.yml
37
.drone.yml
@@ -1,14 +1,31 @@
|
||||
kind: pipeline
|
||||
type: kubernetes
|
||||
name: ci
|
||||
name: container-pipeline
|
||||
|
||||
steps:
|
||||
- name: submodules
|
||||
image: alpine/git
|
||||
commands:
|
||||
- git submodule update --init --recursive
|
||||
- name: test
|
||||
image: cubetiq/openjdk:11u-ubuntu
|
||||
commands:
|
||||
- apt-get update && apt-get install git -y
|
||||
- sh gradlew bootJar
|
||||
- name: docker
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: git.cubetiqs.com/cubetiq/spring-web-modules
|
||||
registry: git.cubetiqs.com
|
||||
tags: latest
|
||||
|
||||
#---
|
||||
#kind: pipeline
|
||||
#type: kubernetes
|
||||
#name: ci
|
||||
#
|
||||
#steps:
|
||||
# - name: submodules
|
||||
# image: alpine/git
|
||||
# commands:
|
||||
# - git submodule update --init --recursive
|
||||
# - name: test
|
||||
# image: cubetiq/openjdk:11u-ubuntu
|
||||
# commands:
|
||||
# - apt-get update && apt-get install git -y
|
||||
# - sh gradlew bootJar
|
||||
47
.github/workflows/ci.yml
vendored
Normal file
47
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['main']
|
||||
pull_request:
|
||||
branches: ['main']
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ secrets.DOCKER_REGISTRY }}
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
docker build . --file Dockerfile --tag registry.ctdn.net/spring-web-api:latest
|
||||
docker push registry.ctdn.net/spring-web-api:latest
|
||||
|
||||
deploy:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Cloning repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Push to dokku
|
||||
uses: dokku/github-action@master
|
||||
with:
|
||||
git_remote_url: 'ssh://dokku@heroku.ctdn.net:2222/spring-web-api-demo'
|
||||
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
branch: main
|
||||
git_push_flags: --force
|
||||
6
.github/workflows/codacy.yml
vendored
6
.github/workflows/codacy.yml
vendored
@@ -35,11 +35,11 @@ jobs:
|
||||
steps:
|
||||
# Checkout the repository to the GitHub Actions runner
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
|
||||
- name: Run Codacy Analysis CLI
|
||||
uses: codacy/codacy-analysis-cli-action@d840f886c4bd4edc059706d09c6a1586111c540b
|
||||
uses: codacy/codacy-analysis-cli-action@9acc82f5c4d097d191a50b89b4a447207d280b14
|
||||
with:
|
||||
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
|
||||
# You can also omit the token and run the tools that support default configurations
|
||||
@@ -55,6 +55,6 @@ jobs:
|
||||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
- name: Upload SARIF results file
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -36,4 +36,6 @@ out/
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
|
||||
uploads/
|
||||
38
Dockerfile
38
Dockerfile
@@ -1,16 +1,40 @@
|
||||
FROM cubetiq/calpine-openjdk11:latest
|
||||
# Builder
|
||||
FROM bellsoft/liberica-openjdk-alpine-musl as builder
|
||||
LABEL maintainer="sombochea@cubetiqs.com"
|
||||
|
||||
# Working directory for application
|
||||
WORKDIR /app
|
||||
|
||||
COPY gradlew ./
|
||||
COPY gradle ./gradle
|
||||
RUN ./gradlew
|
||||
|
||||
|
||||
COPY settings.gradle.kts ./
|
||||
COPY build.gradle.kts ./
|
||||
COPY api/build.gradle.kts ./api/build.gradle.kts
|
||||
COPY api/src ./api/src
|
||||
|
||||
RUN ./gradlew api:bootJar
|
||||
|
||||
# Build for container image
|
||||
FROM bellsoft/liberica-openjre-alpine-musl
|
||||
LABEL maintainer="sombochea@cubetiqs.com"
|
||||
|
||||
# App root path
|
||||
WORKDIR /opt/cubetiq
|
||||
|
||||
# Define mount volumn for data path
|
||||
VOLUME ["/opt/cubetiq/data"]
|
||||
# App volumn
|
||||
VOLUME ["/opt/cubetiq", "/data"]
|
||||
|
||||
# Copy build source to container
|
||||
COPY api/build/libs/*.jar ./api.jar
|
||||
# Api Module Build Directory
|
||||
ARG API_BUILD_DIR=api/build
|
||||
|
||||
ENV APP_DATA_DIR "/opt/cubetiq/data"
|
||||
# Copy the app bundle to the workdir
|
||||
COPY --from=builder /app/${API_BUILD_DIR}/libs/api.jar ./api.jar
|
||||
|
||||
# App profile will run with
|
||||
ENV PROFILE=dev
|
||||
ENV APP_DATA_DIR=/data
|
||||
|
||||
# Entrypoint to app
|
||||
CMD ["java","-jar", "./api.jar"]
|
||||
19
Jenkinsfile
vendored
Normal file
19
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
triggers {
|
||||
pollSCM '* * * * *'
|
||||
}
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh './gradlew assemble'
|
||||
}
|
||||
}
|
||||
stage('BootJar') {
|
||||
steps {
|
||||
sh './gradlew bootJar'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
README.md
10
README.md
@@ -1,16 +1,17 @@
|
||||
# CUBETIQ Web Modules (Template)
|
||||
|
||||
- Setup and Default Web Configuration
|
||||
- Swagger UI and API's Documentation (SpringFox)
|
||||
- Swagger UI and API's Documentation (SpringDoc)
|
||||
- General Purpose for External and Internal use-cases
|
||||
- Dockerfile and Docker profile build support
|
||||
- Kubernetes support
|
||||
- Jenkins Support
|
||||
|
||||
### Language and Framework
|
||||
|
||||
- Spring Boot: 2.6.6
|
||||
- Kotlin: 1.6.20
|
||||
- Gradle: 7.4.1
|
||||
- Spring Boot: 3.0.0
|
||||
- Kotlin: 1.7.22
|
||||
- Gradle: 7.5.1
|
||||
|
||||
# Modules
|
||||
|
||||
@@ -140,7 +141,6 @@ spring:
|
||||
```
|
||||
|
||||
- H2 (Embedded)
|
||||
-
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
|
||||
@@ -1,61 +1,46 @@
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
plugins {
|
||||
id("org.springframework.boot")
|
||||
id("io.spring.dependency-management")
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.spring")
|
||||
kotlin("plugin.jpa")
|
||||
}
|
||||
|
||||
val kotlinVersion = "1.6.20"
|
||||
val springBootVersion = "2.6.6"
|
||||
|
||||
// find the last commit
|
||||
fun getGitHashLastCommit(): String {
|
||||
val stdout = ByteArrayOutputStream()
|
||||
exec {
|
||||
commandLine("git", "rev-parse", "HEAD")
|
||||
standardOutput = stdout
|
||||
}
|
||||
|
||||
return stdout.toString().trim()
|
||||
id("org.springframework.boot")
|
||||
id("io.spring.dependency-management")
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.spring")
|
||||
kotlin("plugin.jpa")
|
||||
}
|
||||
|
||||
springBoot {
|
||||
buildInfo {
|
||||
properties {
|
||||
additional["commitId"] = getGitHashLastCommit()
|
||||
additional["springBootVersion"] = springBootVersion
|
||||
additional["kotlinVersion"] = kotlinVersion
|
||||
}
|
||||
}
|
||||
buildInfo { }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Spring Data JPA (Required for Database Layer)
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
||||
// Spring Data JPA (Required for Database Layer)
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
||||
implementation("org.springframework.boot:spring-boot-starter-validation")
|
||||
|
||||
// Migrating from SpringFox
|
||||
implementation("org.springdoc:springdoc-openapi-ui:1.6.7")
|
||||
// Migrating from SpringDoc API (Swagger) for Support Spring Boot 3.x
|
||||
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2")
|
||||
|
||||
// SPRING FRAMEWORK AND CORE
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||
// SPRING FRAMEWORK AND CORE
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||
|
||||
// Development Runtime
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
// Development Runtime
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
|
||||
runtimeOnly("com.h2database:h2")
|
||||
runtimeOnly("org.postgresql:postgresql")
|
||||
runtimeOnly("com.h2database:h2")
|
||||
// runtimeOnly("org.postgresql:postgresql")
|
||||
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.withType<org.springframework.boot.gradle.tasks.bundling.BootJar> {
|
||||
archiveFileName.set("api.jar")
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.cubetiqs.web.config
|
||||
|
||||
import org.apache.catalina.connector.Connector
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
|
||||
import org.springframework.boot.web.servlet.server.ServletWebServerFactory
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
@ConditionalOnProperty(name = ["http.port"], matchIfMissing = false)
|
||||
@Configuration
|
||||
class HttpServletConfig(
|
||||
@Value("\${http.port:8080}")
|
||||
private val httpPort: Int,
|
||||
) {
|
||||
@Bean
|
||||
fun servletContainer(): ServletWebServerFactory {
|
||||
val tomcat = TomcatServletWebServerFactory()
|
||||
tomcat.addAdditionalTomcatConnectors(createStandardConnector())
|
||||
return tomcat
|
||||
}
|
||||
|
||||
private fun createStandardConnector(): Connector {
|
||||
val connector = Connector("org.apache.coyote.http11.Http11NioProtocol")
|
||||
connector.port = httpPort
|
||||
connector.scheme = "http"
|
||||
return connector
|
||||
}
|
||||
}
|
||||
10
api/src/main/kotlin/com/cubetiqs/web/config/JpaConfig.kt
Normal file
10
api/src/main/kotlin/com/cubetiqs/web/config/JpaConfig.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package com.cubetiqs.web.config
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing
|
||||
|
||||
@ConditionalOnProperty(prefix = "spring.datasource", name = ["enabled"], havingValue = "true")
|
||||
@Configuration
|
||||
@EnableJpaAuditing
|
||||
class JpaConfig
|
||||
@@ -6,16 +6,19 @@ import io.swagger.v3.oas.annotations.security.SecurityScheme
|
||||
import io.swagger.v3.oas.models.OpenAPI
|
||||
import io.swagger.v3.oas.models.info.Info
|
||||
import io.swagger.v3.oas.models.info.License
|
||||
import org.springdoc.core.GroupedOpenApi
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import io.swagger.v3.oas.annotations.OpenAPIDefinition
|
||||
import io.swagger.v3.oas.annotations.servers.Server
|
||||
import org.springdoc.core.models.GroupedOpenApi
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
|
||||
//import org.springdoc.core.SpringDocUtils
|
||||
//import org.springdoc.core.converters.models.Pageable
|
||||
//import javax.servlet.http.HttpServletRequest
|
||||
|
||||
@ConditionalOnProperty(name = ["springdoc.api-docs.enabled"], havingValue = "true")
|
||||
@Configuration
|
||||
@SecurityScheme(
|
||||
name = "bearerAuth",
|
||||
@@ -32,7 +35,6 @@ class OpenApiDocConfig @Autowired constructor(
|
||||
val appProperties: AppProperties,
|
||||
) {
|
||||
companion object {
|
||||
private val ADMIN_API_PATH get() = "/admin/**"
|
||||
private val DEFAULT_API_PATH get() = "/**"
|
||||
}
|
||||
|
||||
@@ -55,19 +57,10 @@ class OpenApiDocConfig @Autowired constructor(
|
||||
return GroupedOpenApi.builder()
|
||||
.group("default")
|
||||
.pathsToMatch(DEFAULT_API_PATH)
|
||||
.pathsToExclude("/error", ADMIN_API_PATH)
|
||||
.packagesToScan("com.cubetiqs.web")
|
||||
.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun adminApi(): GroupedOpenApi {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("admin")
|
||||
.pathsToMatch(ADMIN_API_PATH)
|
||||
.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun cubetiqOpenAPI(): OpenAPI {
|
||||
return OpenAPI()
|
||||
|
||||
@@ -10,6 +10,6 @@ import org.springframework.web.servlet.view.RedirectView
|
||||
class ApiDoc {
|
||||
@GetMapping(value = [ "/api-doc", "/api-docs"])
|
||||
fun redirect(): RedirectView {
|
||||
return RedirectView("/swagger-ui/")
|
||||
return RedirectView("/swagger-ui/index.html")
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,6 @@ class IndexController @Autowired constructor(
|
||||
service = buildProperties.artifact,
|
||||
version = buildProperties.version,
|
||||
date = buildProperties.time.toString(),
|
||||
commit = buildProperties["commitId"],
|
||||
authors = authors,
|
||||
)
|
||||
return response(response)
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.cubetiqs.web.controller.admin
|
||||
|
||||
import com.cubetiqs.web.annotation.ApiBearerAuth
|
||||
import com.cubetiqs.web.controller.BaseController
|
||||
import com.cubetiqs.web.util.RouteConstants
|
||||
import io.swagger.v3.oas.annotations.tags.Tag
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@Tag(name = "Admin")
|
||||
@RestController
|
||||
@RequestMapping(RouteConstants.ADMIN)
|
||||
class AdminController : BaseController {
|
||||
@ApiBearerAuth
|
||||
@GetMapping
|
||||
fun getAdmin(): String {
|
||||
return "Admin"
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ data class ApiInfoResponse(
|
||||
val service: String,
|
||||
val version: String,
|
||||
val date: String,
|
||||
val commit: String,
|
||||
val authors: Collection<ApiInfoAuthorResponse> = listOf(),
|
||||
) : BaseRequestModel
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.cubetiqs.web.modules
|
||||
|
||||
import com.cubetiqs.web.modules.file.FileStorageFactory
|
||||
import com.cubetiqs.web.modules.file.FileStorageLocalProvider
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.context.annotation.Lazy
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
@Lazy(false)
|
||||
class ModuleInitializer constructor(
|
||||
@Value("\${module.uploader.local.path:./uploads}")
|
||||
private val fileBasePath: String,
|
||||
) {
|
||||
init {
|
||||
FileStorageFactory.setProvider(
|
||||
FileStorageLocalProvider(fileBasePath)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.cubetiqs.web.modules.file
|
||||
|
||||
import java.io.File
|
||||
|
||||
open class FileResponse(
|
||||
open var file: File? = null,
|
||||
open var shortPath: String? = null,
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.cubetiqs.web.modules.file
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
object FileStorageFactory {
|
||||
private var provider: FileStorageProvider? = null
|
||||
|
||||
fun setProvider(provider: FileStorageProvider) {
|
||||
FileStorageFactory.provider = provider
|
||||
}
|
||||
|
||||
fun getProvider(): FileStorageProvider {
|
||||
return provider ?: throw IllegalStateException("FileStorageProvider is not set")
|
||||
}
|
||||
|
||||
fun store(file: File): FileResponse {
|
||||
return getProvider().store(file)
|
||||
}
|
||||
|
||||
fun store(file: MultipartFile): FileResponse {
|
||||
val tempPath = System.getProperty("java.io.tmpdir") ?: if (System.getProperty("os.name").lowercase()
|
||||
.contains("win")
|
||||
) "C:\\Windows\\Temp" else "/tmp"
|
||||
val temp = File("$tempPath/${file.originalFilename}")
|
||||
file.transferTo(temp)
|
||||
return store(temp)
|
||||
}
|
||||
|
||||
fun delete(fileName: String) {
|
||||
getProvider().delete(fileName)
|
||||
}
|
||||
|
||||
fun deleteAll() {
|
||||
getProvider().deleteAll()
|
||||
}
|
||||
|
||||
fun get(fileName: String): FileResponse {
|
||||
return getProvider().get(fileName)
|
||||
}
|
||||
|
||||
fun zipAll(): ByteArray? {
|
||||
if (getProvider() is FileStorageZip) {
|
||||
return (getProvider() as FileStorageZip).zip(null)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun unzip(inputStream: InputStream): List<File> {
|
||||
if (getProvider() is FileStorageUnzip) {
|
||||
return (getProvider() as FileStorageUnzip).unzip(inputStream, null)
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.cubetiqs.web.modules.file
|
||||
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
open class FileStorageLocalProvider(
|
||||
private val basePath: String,
|
||||
) : FileStorageProvider, FileStorageZip, FileStorageUnzip {
|
||||
private fun loadBasePath(fileName: String): String {
|
||||
val prefixPath = if (basePath.endsWith("/")) {
|
||||
""
|
||||
} else {
|
||||
"/"
|
||||
}
|
||||
|
||||
return basePath + prefixPath + fileName
|
||||
}
|
||||
|
||||
override fun store(file: File): FileResponse {
|
||||
if (!file.exists()) {
|
||||
throw FileNotFoundException("File not found")
|
||||
}
|
||||
|
||||
val path = loadBasePath(file.name)
|
||||
val savedFile = file.copyTo(File(path), true)
|
||||
return FileResponse(
|
||||
file = savedFile,
|
||||
shortPath = path,
|
||||
)
|
||||
}
|
||||
|
||||
override fun get(fileName: String): FileResponse {
|
||||
val path = loadBasePath(fileName)
|
||||
val file = File(path)
|
||||
if (!file.exists()) {
|
||||
throw FileNotFoundException("File $fileName not found")
|
||||
}
|
||||
|
||||
return FileResponse(
|
||||
file = file,
|
||||
shortPath = path,
|
||||
)
|
||||
}
|
||||
|
||||
override fun delete(fileName: String) {
|
||||
val path = loadBasePath(fileName)
|
||||
val file = File(path)
|
||||
if (file.isFile) {
|
||||
file.delete()
|
||||
} else {
|
||||
throw IllegalArgumentException("File $fileName not found")
|
||||
}
|
||||
}
|
||||
|
||||
override fun deleteAll() {
|
||||
val file = File(basePath)
|
||||
if (file.isDirectory) {
|
||||
file.deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
||||
override fun zip(sourceFolder: String?, os: OutputStream) {
|
||||
FileZipper.zipToStream(sourceFolder ?: basePath, os)
|
||||
}
|
||||
|
||||
override fun zip(sourceFolder: String?): ByteArray {
|
||||
return FileZipper.zipToBytes(sourceFolder ?: basePath)
|
||||
}
|
||||
|
||||
override fun unzip(inputStream: InputStream, destinationFolder: String?): List<File> {
|
||||
return FileZipper.unzip(inputStream, destinationFolder ?: basePath)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.cubetiqs.web.modules.file
|
||||
|
||||
import java.io.File
|
||||
|
||||
interface FileStorageProvider {
|
||||
fun store(file: File): FileResponse
|
||||
fun get(fileName: String): FileResponse
|
||||
fun delete(fileName: String)
|
||||
fun deleteAll()
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.cubetiqs.web.modules.file
|
||||
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
interface FileStorageUnzip {
|
||||
fun unzip(inputStream: InputStream, destinationFolder: String?): List<File>
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.cubetiqs.web.modules.file
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
interface FileStorageZip {
|
||||
fun zip(sourceFolder: String?, os: OutputStream)
|
||||
fun zip(sourceFolder: String?): ByteArray {
|
||||
val os = ByteArrayOutputStream()
|
||||
zip(sourceFolder, os)
|
||||
return os.toByteArray()
|
||||
}
|
||||
}
|
||||
133
api/src/main/kotlin/com/cubetiqs/web/modules/file/FileZipper.kt
Normal file
133
api/src/main/kotlin/com/cubetiqs/web/modules/file/FileZipper.kt
Normal file
@@ -0,0 +1,133 @@
|
||||
package com.cubetiqs.web.modules.file
|
||||
|
||||
import java.io.*
|
||||
import java.nio.file.*
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipInputStream
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
|
||||
object FileZipper {
|
||||
private const val BUFFER = 2048
|
||||
|
||||
fun zip(sourceFolder: String, destFolder: String) {
|
||||
try {
|
||||
val zipFolder = if (destFolder.endsWith(".zip")) {
|
||||
""
|
||||
} else {
|
||||
".zip"
|
||||
}
|
||||
FileOutputStream(zipFolder).use { fos ->
|
||||
zipToStream(sourceFolder, fos)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun zipToStream(sourceFolder: String, os: OutputStream) {
|
||||
ZipOutputStream(os).use { zos ->
|
||||
val sourcePath: Path = Paths.get(sourceFolder)
|
||||
// Walk the tree structure using WalkFileTree method
|
||||
Files.walkFileTree(sourcePath, object : SimpleFileVisitor<Path>() {
|
||||
@Throws(IOException::class)
|
||||
override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
|
||||
if (sourcePath != dir) {
|
||||
zos.putNextEntry(ZipEntry(sourcePath.relativize(dir).toString() + "/"))
|
||||
zos.closeEntry()
|
||||
}
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
|
||||
zos.putNextEntry(ZipEntry(sourcePath.relativize(file).toString()))
|
||||
Files.copy(file, zos)
|
||||
zos.closeEntry()
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun zipToBytes(sourceFolder: String): ByteArray {
|
||||
val bos = ByteArrayOutputStream()
|
||||
zipToStream(sourceFolder, bos)
|
||||
return bos.toByteArray()
|
||||
}
|
||||
|
||||
fun unzipFromFile(sourceZipFile: File, destFolder: String) {
|
||||
try {
|
||||
val zipFile = ZipFile(sourceZipFile)
|
||||
val entries = zipFile.entries()
|
||||
while (entries.hasMoreElements()) {
|
||||
val entry = entries.nextElement()
|
||||
val destFile = File(destFolder, entry.name)
|
||||
if (entry.isDirectory) {
|
||||
destFile.mkdirs()
|
||||
} else {
|
||||
destFile.parentFile.mkdirs()
|
||||
val istream = zipFile.getInputStream(entry)
|
||||
val ostream = FileOutputStream(destFile)
|
||||
val buffer = ByteArray(BUFFER)
|
||||
var len: Int
|
||||
while (istream.read(buffer).also { len = it } > 0) {
|
||||
ostream.write(buffer, 0, len)
|
||||
}
|
||||
ostream.close()
|
||||
istream.close()
|
||||
}
|
||||
}
|
||||
zipFile.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun unzip(fis: InputStream, destFolder: String): List<File> {
|
||||
val extractFiles = mutableListOf<File>()
|
||||
|
||||
try {
|
||||
val root = File(destFolder)
|
||||
if (!root.exists()) {
|
||||
root.mkdir()
|
||||
}
|
||||
val zis = ZipInputStream(BufferedInputStream(fis))
|
||||
var entry: ZipEntry?
|
||||
while (zis.nextEntry.also { entry = it } != null) {
|
||||
val fileName = entry?.name
|
||||
val file = File(destFolder + File.separator.toString() + fileName)
|
||||
if (entry?.isDirectory == false) {
|
||||
extractFileContentFromArchive(file, zis)
|
||||
} else {
|
||||
if (!file.exists()) {
|
||||
file.mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
extractFiles.add(file)
|
||||
zis.closeEntry()
|
||||
}
|
||||
zis.close()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return extractFiles
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun extractFileContentFromArchive(file: File, zis: ZipInputStream) {
|
||||
val fos = FileOutputStream(file)
|
||||
val bos = BufferedOutputStream(fos, BUFFER)
|
||||
var len: Int
|
||||
val data = ByteArray(BUFFER)
|
||||
while (zis.read(data, 0, BUFFER).also { len = it } != -1) {
|
||||
bos.write(data, 0, len)
|
||||
}
|
||||
bos.flush()
|
||||
bos.close()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.cubetiqs.web.modules.uploader
|
||||
|
||||
import com.cubetiqs.web.modules.file.FileStorageFactory
|
||||
import com.cubetiqs.web.util.RouteConstants
|
||||
import io.swagger.v3.oas.annotations.Operation
|
||||
import io.swagger.v3.oas.annotations.Parameter
|
||||
import io.swagger.v3.oas.annotations.tags.Tag
|
||||
import org.springdoc.core.converters.models.PageableAsQueryParam
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.data.domain.Page
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.util.FileCopyUtils
|
||||
import org.springframework.web.bind.annotation.*
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.util.*
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
|
||||
@UploaderModule
|
||||
@Tag(name = "Uploader Controller")
|
||||
@RestController
|
||||
@RequestMapping(RouteConstants.INDEX + "uploader")
|
||||
class UploaderController @Autowired constructor(
|
||||
private val repository: UploaderRepository,
|
||||
) {
|
||||
@GetMapping
|
||||
@PageableAsQueryParam
|
||||
@Operation(summary = "Get all files")
|
||||
fun getAll(
|
||||
@Parameter(hidden = true)
|
||||
pageable: Pageable?,
|
||||
): Page<UploaderEntity> {
|
||||
return repository.findAll(pageable ?: Pageable.unpaged())
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.OK)
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "Get a file by id")
|
||||
fun get(
|
||||
@PathVariable id: String,
|
||||
): UploaderEntity {
|
||||
val entity = repository.findById(UUID.fromString(id)).orElseThrow {
|
||||
throw IllegalArgumentException("File not found")
|
||||
}
|
||||
return repository.save(entity)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.OK)
|
||||
@GetMapping("/{id}/stream", produces = [MediaType.APPLICATION_OCTET_STREAM_VALUE])
|
||||
@Operation(summary = "Get file stream by id")
|
||||
fun stream(
|
||||
@PathVariable id: String,
|
||||
@RequestParam(required = false, value = "download") download: Boolean?,
|
||||
response: HttpServletResponse,
|
||||
) {
|
||||
val entity = repository.findById(UUID.fromString(id)).orElseThrow {
|
||||
throw IllegalArgumentException("User not found")
|
||||
}
|
||||
|
||||
if (!entity.isFileExists()) {
|
||||
throw IllegalArgumentException("File not found")
|
||||
}
|
||||
|
||||
val file = entity.getFile() ?: throw IllegalArgumentException("File source not found")
|
||||
val disposition = if (download == true) {
|
||||
"attachment"
|
||||
} else {
|
||||
"inline"
|
||||
}
|
||||
|
||||
response.setHeader("Content-Disposition", "$disposition; filename=\"${entity.filename}\"")
|
||||
response.contentType = entity.contentType
|
||||
response.setContentLengthLong(file.length())
|
||||
|
||||
FileCopyUtils.copy(file.readBytes(), response.outputStream)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.OK)
|
||||
@GetMapping("/zip", produces = [MediaType.APPLICATION_OCTET_STREAM_VALUE])
|
||||
@Operation(summary = "Zip all files")
|
||||
fun zipAll(
|
||||
@RequestParam(required = false, value = "filename", defaultValue = "files") filename: String?,
|
||||
response: HttpServletResponse,
|
||||
) {
|
||||
val zipBytes = FileStorageFactory.zipAll() ?: throw IllegalArgumentException("Zip file not found")
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"$filename.zip\"")
|
||||
response.contentType = "application/zip"
|
||||
response.setContentLengthLong(zipBytes.size.toLong())
|
||||
|
||||
FileCopyUtils.copy(zipBytes, response.outputStream)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.CREATED)
|
||||
@PostMapping(consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
|
||||
@Operation(summary = "Upload a file")
|
||||
fun create(
|
||||
@RequestPart file: MultipartFile,
|
||||
): UploaderEntity {
|
||||
val entity = UploaderEntity.fromFile(file)
|
||||
return repository.save(entity)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.CREATED)
|
||||
@PostMapping("/unzip", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
|
||||
@Operation(summary = "Unzip a file")
|
||||
fun unzip(
|
||||
@RequestPart file: MultipartFile,
|
||||
): List<UploaderEntity> {
|
||||
val files = FileStorageFactory.unzip(file.inputStream)
|
||||
.map { UploaderEntity.fromFileWithoutStore(it) }
|
||||
return repository.saveAll(files)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.OK)
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "Update a file by id")
|
||||
fun update(
|
||||
@PathVariable id: String,
|
||||
@RequestBody body: UploaderEntity
|
||||
): UploaderEntity {
|
||||
val entity = repository.findById(UUID.fromString(id)).orElseThrow {
|
||||
throw IllegalArgumentException("File not found")
|
||||
}
|
||||
body.id = entity.id
|
||||
return repository.save(body)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.NO_CONTENT)
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "Delete a file by id")
|
||||
fun delete(
|
||||
@PathVariable id: String,
|
||||
) {
|
||||
val entity = repository.findById(UUID.fromString(id)).orElseThrow {
|
||||
throw IllegalArgumentException("File not found")
|
||||
}
|
||||
if (!entity.path.isNullOrEmpty()) {
|
||||
FileStorageFactory.delete(entity.path!!)
|
||||
}
|
||||
repository.delete(entity)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.NO_CONTENT)
|
||||
@DeleteMapping
|
||||
@Operation(summary = "Delete all files")
|
||||
fun deleteAll() {
|
||||
FileStorageFactory.deleteAll()
|
||||
repository.deleteAll()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package com.cubetiqs.web.modules.uploader
|
||||
|
||||
import com.cubetiqs.web.modules.file.FileStorageFactory
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import org.hibernate.Hibernate
|
||||
import org.springframework.data.annotation.CreatedDate
|
||||
import org.springframework.data.annotation.LastModifiedDate
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.Serializable
|
||||
import java.nio.file.Files
|
||||
import java.util.*
|
||||
import jakarta.persistence.*
|
||||
import org.hibernate.annotations.GenericGenerator
|
||||
|
||||
@UploaderModule
|
||||
@Entity
|
||||
@Table(name = "`uploader`")
|
||||
@EntityListeners(AuditingEntityListener::class)
|
||||
open class UploaderEntity(
|
||||
@Id
|
||||
@GeneratedValue(generator = "custom-uuid")
|
||||
@GenericGenerator(
|
||||
name = "custom-uuid",
|
||||
strategy = "org.hibernate.id.UUIDGenerator",
|
||||
parameters = [org.hibernate.annotations.Parameter(name = "uuid_gen_strategy_class", value = "org.hibernate.id.uuid.CustomVersionOneStrategy")]
|
||||
)
|
||||
@Column(columnDefinition = "BINARY(16)")
|
||||
open var id: UUID? = null,
|
||||
|
||||
@Column(name = "`filename`")
|
||||
open var filename: String? = null,
|
||||
|
||||
@Column(name = "`content_type`")
|
||||
open var contentType: String? = null,
|
||||
|
||||
@Column(name = "`content_length`")
|
||||
open var contentLength: Long? = null,
|
||||
|
||||
@Column(name = "`path`", length = 300)
|
||||
open var path: String? = null,
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "`created_at`")
|
||||
@CreatedDate
|
||||
open var createdAt: Date? = null,
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "`updated_at`")
|
||||
@LastModifiedDate
|
||||
open var updatedAt: Date? = null,
|
||||
|
||||
@Column(length = 30, name = "`provider_type`")
|
||||
open var providerType: String? = null,
|
||||
) : Serializable {
|
||||
@Transient
|
||||
@JsonIgnore
|
||||
private var partFile: MultipartFile? = null
|
||||
|
||||
@Transient
|
||||
@JsonIgnore
|
||||
private var file: File? = null
|
||||
|
||||
@Transient
|
||||
fun isFileExists(): Boolean {
|
||||
val temp = getFile()
|
||||
return temp?.exists() ?: false
|
||||
}
|
||||
|
||||
@Transient
|
||||
@JsonIgnore
|
||||
fun getFile(): File? {
|
||||
if (file == null) {
|
||||
file = File(path ?: return null)
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false
|
||||
other as UploaderEntity
|
||||
|
||||
return id != null && id == other.id
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = javaClass.hashCode()
|
||||
|
||||
companion object {
|
||||
fun fromFile(file: MultipartFile): UploaderEntity {
|
||||
// transfer to file storage first
|
||||
val store = FileStorageFactory.store(file)
|
||||
val uploader = UploaderEntity()
|
||||
uploader.partFile = file
|
||||
uploader.providerType = "local"
|
||||
uploader.filename = file.originalFilename
|
||||
uploader.contentType = file.contentType
|
||||
uploader.contentLength = file.size
|
||||
uploader.path = store.shortPath
|
||||
return uploader
|
||||
}
|
||||
|
||||
fun fromFileWithoutStore(file: File): UploaderEntity {
|
||||
val uploader = UploaderEntity()
|
||||
uploader.file = file
|
||||
uploader.providerType = "local"
|
||||
uploader.filename = file.name
|
||||
uploader.contentType = Files.probeContentType(file.toPath())
|
||||
uploader.contentLength = file.length()
|
||||
uploader.path = file.path
|
||||
return uploader
|
||||
}
|
||||
|
||||
fun fromUploader(uploader: UploaderEntity): MultipartFile? {
|
||||
if (uploader.partFile != null) {
|
||||
return uploader.partFile
|
||||
}
|
||||
|
||||
val file = try {
|
||||
File(uploader.path!!)
|
||||
} catch (ex: Exception) {
|
||||
null
|
||||
} ?: return null
|
||||
|
||||
return object : MultipartFile {
|
||||
override fun getInputStream(): InputStream {
|
||||
return file.inputStream()
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return file.name
|
||||
}
|
||||
|
||||
override fun getOriginalFilename(): String? {
|
||||
return uploader.filename
|
||||
}
|
||||
|
||||
override fun getContentType(): String? {
|
||||
return uploader.contentType
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return file.length() == 0L
|
||||
}
|
||||
|
||||
override fun getSize(): Long {
|
||||
return file.length()
|
||||
}
|
||||
|
||||
override fun getBytes(): ByteArray {
|
||||
return file.readBytes()
|
||||
}
|
||||
|
||||
override fun transferTo(dest: File) {
|
||||
file.copyTo(dest)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.cubetiqs.web.modules.uploader
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
|
||||
@ConditionalOnProperty(name = ["modules.uploader.enabled", "spring.datasource.enabled"], havingValue = "true")
|
||||
annotation class UploaderModule
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.cubetiqs.web.modules.uploader
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.util.*
|
||||
|
||||
@UploaderModule
|
||||
@Repository
|
||||
interface UploaderRepository : JpaRepository<UploaderEntity, UUID>
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.cubetiqs.web.modules.user
|
||||
|
||||
import com.cubetiqs.web.util.RouteConstants
|
||||
import io.swagger.v3.oas.annotations.Operation
|
||||
import io.swagger.v3.oas.annotations.Parameter
|
||||
import io.swagger.v3.oas.annotations.tags.Tag
|
||||
import org.springdoc.core.converters.models.PageableAsQueryParam
|
||||
@@ -15,46 +16,69 @@ import java.util.*
|
||||
@RestController
|
||||
@RequestMapping(RouteConstants.INDEX + "user")
|
||||
class UserController @Autowired constructor(
|
||||
private val userRepository: UserRepository,
|
||||
private val repository: UserRepository,
|
||||
) {
|
||||
@GetMapping
|
||||
@PageableAsQueryParam
|
||||
@Operation(summary = "Get all users")
|
||||
fun getAll(
|
||||
@Parameter(hidden = true)
|
||||
pageable: Pageable?,
|
||||
): Page<UserEntity> {
|
||||
return userRepository.findAll(pageable ?: Pageable.unpaged())
|
||||
return repository.findAll(pageable ?: Pageable.unpaged())
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.OK)
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "Get a user by id")
|
||||
fun get(
|
||||
@PathVariable id: String,
|
||||
): UserEntity {
|
||||
val entity = repository.findById(UUID.fromString(id)).orElseThrow {
|
||||
throw IllegalArgumentException("User not found")
|
||||
}
|
||||
return repository.save(entity)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.CREATED)
|
||||
@PostMapping
|
||||
@Operation(summary = "Create a user")
|
||||
fun create(
|
||||
@RequestBody body: UserEntity
|
||||
): UserEntity {
|
||||
return userRepository.save(body)
|
||||
return repository.save(body)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.OK)
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "Update a user by id")
|
||||
fun update(
|
||||
@PathVariable id: String,
|
||||
@RequestBody body: UserEntity
|
||||
): UserEntity {
|
||||
val user = userRepository.findById(UUID.fromString(id)).orElseThrow {
|
||||
val entity = repository.findById(UUID.fromString(id)).orElseThrow {
|
||||
throw IllegalArgumentException("User not found")
|
||||
}
|
||||
body.id = user.id
|
||||
return userRepository.save(body)
|
||||
body.id = entity.id
|
||||
return repository.save(body)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.NO_CONTENT)
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "Delete user by id")
|
||||
fun delete(
|
||||
@PathVariable id: String,
|
||||
) {
|
||||
val user = userRepository.findById(UUID.fromString(id)).orElseThrow {
|
||||
val entity = repository.findById(UUID.fromString(id)).orElseThrow {
|
||||
throw IllegalArgumentException("User not found")
|
||||
}
|
||||
userRepository.delete(user)
|
||||
repository.delete(entity)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.NO_CONTENT)
|
||||
@DeleteMapping
|
||||
@Operation(summary = "Delete all users")
|
||||
fun deleteAll() {
|
||||
repository.deleteAll()
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,27 @@ package com.cubetiqs.web.modules.user
|
||||
import org.hibernate.Hibernate
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
import javax.persistence.*
|
||||
import jakarta.persistence.*
|
||||
import org.hibernate.annotations.GenericGenerator
|
||||
|
||||
@UserModule
|
||||
@Entity
|
||||
@Table(name = "user")
|
||||
@Table(name = "`user`")
|
||||
open class UserEntity(
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@GeneratedValue(generator = "custom-uuid")
|
||||
@GenericGenerator(
|
||||
name = "custom-uuid",
|
||||
strategy = "org.hibernate.id.UUIDGenerator",
|
||||
parameters = [org.hibernate.annotations.Parameter(name = "uuid_gen_strategy_class", value = "org.hibernate.id.uuid.CustomVersionOneStrategy")]
|
||||
)
|
||||
@Column(columnDefinition = "BINARY(16)")
|
||||
open var id: UUID? = null,
|
||||
|
||||
@Column(name = "name", length = 50)
|
||||
@Column(name = "`name`", length = 50)
|
||||
open var name: String? = null,
|
||||
|
||||
@Column(name = "username", length = 50, unique = true)
|
||||
@Column(name = "`username`", length = 50, unique = true)
|
||||
open var username: String? = null,
|
||||
) : Serializable {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
||||
@@ -2,5 +2,5 @@ package com.cubetiqs.web.modules.user
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
|
||||
@ConditionalOnProperty(name = ["module.user.enabled", "spring.datasource.enabled"], havingValue = "true")
|
||||
@ConditionalOnProperty(name = ["modules.user.enabled", "spring.datasource.enabled"], havingValue = "true")
|
||||
annotation class UserModule
|
||||
@@ -1,2 +1 @@
|
||||
server:
|
||||
port: ${APP_PORT:8080}
|
||||
# Custom props with dev's profile
|
||||
@@ -1,12 +1,20 @@
|
||||
http:
|
||||
port: ${PORT:8080}
|
||||
server:
|
||||
port: ${SERVER_PORT:8443}
|
||||
ssl:
|
||||
key-store: ${SERVER_SSL_KEY_STORE:classpath:keystore/server.jks}
|
||||
key-store-password: ${SERVER_SSL_KEY_STORE_PASSWORD:cubetiq}
|
||||
key-password: ${SERVER_SSL_KEY_PASSWORD:cubetiq}
|
||||
enabled: ${SERVER_SSL_ENABLED:false}
|
||||
compression:
|
||||
enabled: ${SERVER_COMPRESSION_ENABLED:${server.ssl.enabled}}
|
||||
|
||||
spring:
|
||||
profiles:
|
||||
active: ${APP_PROFILE:demo}
|
||||
application:
|
||||
name: cubetiq-api-service
|
||||
redis:
|
||||
enabled: ${REDIS_ENABLED:false}
|
||||
host: ${REDIS_HOST:localhost}
|
||||
password: ${REDIS_PASSWORD:null}
|
||||
name: ${SERVICE_NAME:cubetiq-api-service}
|
||||
datasource:
|
||||
enabled: ${DATASOURCE_ENABLED:true}
|
||||
driverClassName: ${DATASOURCE_DRIVER_CLASS_NAME:org.h2.Driver}
|
||||
@@ -15,17 +23,30 @@ spring:
|
||||
password: ${DATASOURCE_PASSWORD:password}
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: ${JPA_HIBERNATE_DDL_AUTO:update}
|
||||
ddl-auto: ${JPA_HIBERNATE_DDL_AUTO:create-drop}
|
||||
open-in-view: ${JPA_OPEN_IN_VIEW:false}
|
||||
database-platform: ${JPA_DATABASE_PLATFORM:org.hibernate.dialect.H2Dialect}
|
||||
data:
|
||||
redis:
|
||||
enabled: ${REDIS_ENABLED:false}
|
||||
host: ${REDIS_HOST:localhost}
|
||||
password: ${REDIS_PASSWORD:null}
|
||||
repositories:
|
||||
enabled: ${DATA_REDIS_REPOSITORIES_ENABLED:false}
|
||||
|
||||
module:
|
||||
modules:
|
||||
user:
|
||||
enabled: ${MODULE_USER_ENABLED:true}
|
||||
uploader:
|
||||
enabled: ${MODULE_UPLOADER_ENABLED:true}
|
||||
local:
|
||||
path: ${MODULE_UPLOADER_FILE_PATH:${cubetiq.app.data-dir}/uploads}
|
||||
|
||||
cubetiq:
|
||||
app:
|
||||
data-dir: ${APP_DATA_DIR:${user.home}/${spring.application.name}}
|
||||
name: CUBETIQ Web API
|
||||
description: CUBETIQ Spring Web API's Documentation
|
||||
name: ${APP_NAME:CUBETIQ Web API}
|
||||
description: ${APP_DESCRIPTION:CUBETIQ Spring Web API's Documentation}
|
||||
|
||||
logging:
|
||||
file:
|
||||
@@ -34,6 +55,4 @@ logging:
|
||||
|
||||
springdoc:
|
||||
api-docs:
|
||||
enabled: true
|
||||
swagger-ui:
|
||||
path: /swagger-ui
|
||||
enabled: ${API_DOCS_ENABLED:true}
|
||||
|
||||
BIN
api/src/main/resources/keystore/server.jks
Normal file
BIN
api/src/main/resources/keystore/server.jks
Normal file
Binary file not shown.
BIN
api/src/main/resources/keystore/server.p12
Normal file
BIN
api/src/main/resources/keystore/server.p12
Normal file
Binary file not shown.
@@ -6,6 +6,6 @@ APP_PROFILE=demo
|
||||
VERSION=demo
|
||||
IMAGE=spring-web-api
|
||||
CONTAINER=$IMAGE
|
||||
REGISTRY=registry.kh.cubetiqs.com
|
||||
REGISTRY=registry.ctdn.net
|
||||
EXPOSE_PORT=8080
|
||||
ROOT_HUB=$REGISTRY/$IMAGE:$VERSION
|
||||
@@ -1,35 +1,25 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
id("org.springframework.boot") version "2.6.6" apply false
|
||||
id("io.spring.dependency-management") version "1.0.11.RELEASE" apply false
|
||||
kotlin("jvm") version "1.6.20" apply false
|
||||
kotlin("plugin.spring") version "1.6.20" apply false
|
||||
kotlin("plugin.jpa") version "1.6.20" apply false
|
||||
id("org.springframework.boot") version "3.0.4" apply false
|
||||
id("io.spring.dependency-management") version "1.1.0" apply false
|
||||
kotlin("jvm") version "1.8.10" apply false
|
||||
kotlin("plugin.spring") version "1.8.10" apply false
|
||||
kotlin("plugin.jpa") version "1.8.10" apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
// Fixed Zero-Day CVE-2021-44228: https://cubetiq.atlassian.net/browse/CERT-1
|
||||
// Fixed Zero-Day CVE-2021-45046: https://cubetiq.atlassian.net/browse/CERT-3
|
||||
// Fixed Zero-Day CVE-2021-45105: https://cubetiq.atlassian.net/browse/CERT-4
|
||||
ext["log4j2.version"] = "2.17.0"
|
||||
|
||||
// Fixed Zero-Day CVE-2021-42550: https://cubetiq.atlassian.net/browse/CERT-5
|
||||
ext["logback.version"] = "1.2.9"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://m.ctdn.net")
|
||||
}
|
||||
|
||||
group = "com.cubetiqs"
|
||||
version = "0.0.1-SNAPSHOT"
|
||||
|
||||
val javaVersion = "11"
|
||||
val javaVersion = "17"
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
sourceCompatibility = javaVersion
|
||||
targetCompatibility = javaVersion
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
@@ -50,4 +40,4 @@ subprojects {
|
||||
mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
18
generate-server-cert.sh
Executable file
18
generate-server-cert.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
CERT_NAME="${1:-server}"
|
||||
CERT_SIZE="${2:-2048}"
|
||||
CERT_DAYS="${3:-3650}"
|
||||
DEST_DIR="${4:-./}"
|
||||
|
||||
echo "Create directory ${DEST_DIR}"
|
||||
mkdir -p "${DEST_DIR}"
|
||||
|
||||
echo "Generating server keystore ${CERT_NAME}.p12 with size: ${CERT_SIZE} days: ${CERT_DAYS}"
|
||||
keytool -genkeypair -alias ${CERT_NAME} -keyalg RSA -keysize ${CERT_SIZE} -storetype PKCS12 -keystore ${DEST_DIR}${CERT_NAME}.p12 -validity ${CERT_DAYS}
|
||||
|
||||
echo "Generating server keystore ${CERT_NAME}.jks with size: ${CERT_SIZE} days: ${CERT_DAYS}"
|
||||
keytool -genkeypair -alias ${CERT_NAME} -keyalg RSA -keysize ${CERT_SIZE} -keystore ${DEST_DIR}${CERT_NAME}.jks -validity ${CERT_DAYS}
|
||||
|
||||
echo "Import keystore ${CERT_NAME}.jks into ${CERT_NAME}.p12"
|
||||
keytool -importkeystore -srckeystore ${DEST_DIR}${CERT_NAME}.jks -destkeystore ${DEST_DIR}${CERT_NAME}.p12 -deststoretype pkcs12
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https://mirror.ctdn.net/gradle/gradle-7.4.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
285
gradlew
vendored
285
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -17,67 +17,101 @@
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
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"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
@@ -106,80 +140,105 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
15
gradlew.bat
vendored
15
gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
@@ -36,6 +36,10 @@ spec:
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: spring-web-dev-secret
|
||||
volumeMounts:
|
||||
- name: spring-web-api-data
|
||||
mountPath: /opt/cubetiq/data
|
||||
subPath: spring-web-api-data
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
@@ -43,4 +47,8 @@ spec:
|
||||
memory: 300Mi
|
||||
limits:
|
||||
cpu: 800m
|
||||
memory: 800Mi
|
||||
memory: 800Mi
|
||||
volumes:
|
||||
- name: spring-web-api-data
|
||||
persistentVolumeClaim:
|
||||
claimName: spring-web-dev-volume
|
||||
|
||||
6
renovate.json
Normal file
6
renovate.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
}
|
||||
1
system.properties
Normal file
1
system.properties
Normal file
@@ -0,0 +1 @@
|
||||
java.runtime.version=zulu-17
|
||||
Reference in New Issue
Block a user