Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
0aeaac17a4
|
|||
|
a2469c7573
|
|||
|
62fcce3005
|
|||
|
c18a108a2b
|
@@ -56,18 +56,30 @@ jobs:
|
|||||||
- name: Generate image tags
|
- name: Generate image tags
|
||||||
id: meta
|
id: meta
|
||||||
run: |
|
run: |
|
||||||
DOCKERHUB_USER="${{ secrets.DOCKER_HUB_USERNAME }}"
|
DOCKERHUB_USER="${{ vars.DOCKER_HUB_USERNAME }}"
|
||||||
REPO_NAME=$(echo "${{ gitea.repository.name }}" | tr '[:upper:]' '[:lower:]')
|
echo "tag_version=${DOCKERHUB_USER}/${APP_NAME}:${{ gitea.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
|
||||||
echo "tag_version=${DOCKERHUB_USER}/${REPO_NAME}:${{ gitea.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
|
echo "tag_latest=${DOCKERHUB_USER}/${APP_NAME}:latest" >> "$GITHUB_OUTPUT"
|
||||||
echo "tag_latest=${DOCKERHUB_USER}/${REPO_NAME}:latest" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: Dockerfile.ci
|
file: Dockerfile.ci
|
||||||
build-args: JAR_FILE=${{ steps.jar.outputs.file }}
|
build-args: |
|
||||||
|
JAR_FILE=${{ steps.jar.outputs.file }}
|
||||||
|
IMAGE_VERSION=${{ gitea.event.release.tag_name }}
|
||||||
|
IMAGE_VENDOR=${{ vars.VENDOR }}
|
||||||
|
BUILD_DATE=${{ gitea.event.release.published_at }}
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
${{ steps.meta.outputs.tag_version }}
|
${{ steps.meta.outputs.tag_version }}
|
||||||
${{ steps.meta.outputs.tag_latest }}
|
${{ steps.meta.outputs.tag_latest }}
|
||||||
|
|
||||||
|
- name: Push README to Docker Hub
|
||||||
|
run: >
|
||||||
|
docker run --rm
|
||||||
|
-v "$(pwd)/README.md:/workspace/README.md:ro"
|
||||||
|
-e DOCKER_USER="${{ vars.DOCKER_HUB_USERNAME }}"
|
||||||
|
-e DOCKER_PASS="${{ secrets.DOCKER_HUB_TOKEN }}"
|
||||||
|
chko/docker-pushrm:latest
|
||||||
|
"${{ vars.DOCKER_HUB_USERNAME }}/${{ env.APP_NAME }}"
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
variables:
|
|
||||||
GRADLE_OPTS: -Dorg.gradle.daemon=false
|
|
||||||
DOCKER_HOST: unix:///var/run/docker.sock
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- release
|
|
||||||
|
|
||||||
release:
|
|
||||||
stage: release
|
|
||||||
image: amazoncorretto:21-alpine
|
|
||||||
cache:
|
|
||||||
key: gradle
|
|
||||||
paths:
|
|
||||||
- .gradle/wrapper
|
|
||||||
- .gradle/caches
|
|
||||||
before_script:
|
|
||||||
- chmod +x gradlew
|
|
||||||
- apk add --no-cache docker-cli
|
|
||||||
script:
|
|
||||||
- ./gradlew bootJar -x test -PartefactVersion="$CI_COMMIT_TAG"
|
|
||||||
- JAR_FILE=$(find build/libs -name '*.jar' | head -1)
|
|
||||||
- echo "Building Docker image for tag $CI_COMMIT_TAG with JAR $JAR_FILE"
|
|
||||||
- docker build
|
|
||||||
-f Dockerfile.ci
|
|
||||||
--build-arg JAR_FILE="$JAR_FILE"
|
|
||||||
-t "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"
|
|
||||||
.
|
|
||||||
- docker tag "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG" "$CI_REGISTRY_IMAGE:latest"
|
|
||||||
- echo "Pushing image $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"
|
|
||||||
- docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
|
|
||||||
- docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"
|
|
||||||
- docker push "$CI_REGISTRY_IMAGE:latest"
|
|
||||||
rules:
|
|
||||||
- if: $CI_COMMIT_TAG
|
|
||||||
@@ -2,6 +2,18 @@ FROM amazoncorretto:21-alpine
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ARG JAR_FILE
|
ARG JAR_FILE
|
||||||
|
ARG IMAGE_VERSION=dev
|
||||||
|
ARG IMAGE_VENDOR=OnixByte
|
||||||
|
ARG BUILD_DATE
|
||||||
|
|
||||||
COPY ${JAR_FILE} app.jar
|
COPY ${JAR_FILE} app.jar
|
||||||
|
|
||||||
|
LABEL org.opencontainers.image.title="delta-force-guide-server" \
|
||||||
|
org.opencontainers.image.description="REST API backend for managing Delta Force game firearm builds and modifications" \
|
||||||
|
org.opencontainers.image.version="${IMAGE_VERSION}" \
|
||||||
|
org.opencontainers.image.vendor="${IMAGE_VENDOR}" \
|
||||||
|
org.opencontainers.image.source="https://git.onixbyte.com/onixbyte/delta-force-guide-server" \
|
||||||
|
org.opencontainers.image.licenses="MIT" \
|
||||||
|
org.opencontainers.image.created="${BUILD_DATE}"
|
||||||
|
|
||||||
ENTRYPOINT ["java", "-jar", "app.jar"]
|
ENTRYPOINT ["java", "-jar", "app.jar"]
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ dependencies {
|
|||||||
|
|
||||||
tasks.processResources {
|
tasks.processResources {
|
||||||
filesMatching("application.yaml") {
|
filesMatching("application.yaml") {
|
||||||
|
println("appVersion = ${artefactVersion}, channel = ${buildChannel}, vendor = ${vendor}")
|
||||||
expand(
|
expand(
|
||||||
"appVersion" to artefactVersion,
|
"appVersion" to artefactVersion,
|
||||||
"channel" to buildChannel,
|
"channel" to buildChannel,
|
||||||
|
|||||||
@@ -9,15 +9,19 @@ spring:
|
|||||||
host: localhost
|
host: localhost
|
||||||
port: 6379
|
port: 6379
|
||||||
database: 0
|
database: 0
|
||||||
# password: 6hLFVqfGPviTYukn # Uncomment if password is necessary
|
# password: qwerty # Uncomment if password is necessary
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
pattern:
|
pattern:
|
||||||
# dateformat: dd MMM yyyy HH:mm:ss.SSS # Modify this for custom date format.
|
# dateformat: dd MMM yyyy HH:mm:ss.SSS # Modify this for custom date format.
|
||||||
|
|
||||||
app:
|
app:
|
||||||
common:
|
cookie:
|
||||||
version: 1.3.0.8-dev # Application version, you can change to any version you like, used for communication with frontend.
|
http-only: true
|
||||||
|
secure: false
|
||||||
|
same-site: none
|
||||||
|
path: '/'
|
||||||
|
max-age: P1D
|
||||||
cors:
|
cors:
|
||||||
allowed-origins: # Cross-origin allowed origins
|
allowed-origins: # Cross-origin allowed origins
|
||||||
- "http://localhost:5173" # Dev server for vite.
|
- "http://localhost:5173" # Dev server for vite.
|
||||||
@@ -41,12 +45,10 @@ app:
|
|||||||
issuer: dfguide.local # Issuer host
|
issuer: dfguide.local # Issuer host
|
||||||
secret: qwertyuiopasdfghjklzxcvbnm123456 # JWT singing secret, a 32-byte long or longer string is recommended
|
secret: qwertyuiopasdfghjklzxcvbnm123456 # JWT singing secret, a 32-byte long or longer string is recommended
|
||||||
valid-time: PT2H # JWT valid duration
|
valid-time: PT2H # JWT valid duration
|
||||||
cookie: # Cookie settings.
|
webhook:
|
||||||
http-only: true
|
github:
|
||||||
secure: false
|
secret: 123456
|
||||||
same-site: lax
|
allowed-users: [ octotcat, onixbyte ]
|
||||||
path: '/'
|
|
||||||
max-age: PT2H
|
|
||||||
|
|
||||||
springdoc:
|
springdoc:
|
||||||
api-docs:
|
api-docs:
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
info = @Info(
|
info = @Info(
|
||||||
title = "Delta Force Guide Server",
|
title = "Delta Force Guide Server",
|
||||||
description = "API for managing Delta Force game firearm builds",
|
description = "API for managing Delta Force game firearm builds",
|
||||||
version = "1.3.4",
|
version = "1.4.0",
|
||||||
contact = @Contact(
|
contact = @Contact(
|
||||||
name = "Zihlu Wang",
|
name = "Zihlu Wang",
|
||||||
email = "zihlu.wang@onixbyte.com"
|
email = "zihlu.wang@onixbyte.com"
|
||||||
),
|
),
|
||||||
license = @License(
|
license = @License(
|
||||||
name = "MIT",
|
name = "MIT",
|
||||||
url = "https://git.onixbyte.cn/onixbyte/delta-force-guide-server/-/raw/main/LICENCE"
|
url = "https://onixbyte.dev/onixbyte/delta-force-guide-server/raw/branch/main/LICENCE"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import com.onixbyte.deltaforceguide.filter.TokenAuthenticationFilter;
|
|||||||
import com.onixbyte.deltaforceguide.properties.CookieProperties;
|
import com.onixbyte.deltaforceguide.properties.CookieProperties;
|
||||||
import com.onixbyte.deltaforceguide.properties.TokenProperties;
|
import com.onixbyte.deltaforceguide.properties.TokenProperties;
|
||||||
import com.onixbyte.deltaforceguide.security.provider.UsernamePasswordAuthenticationProvider;
|
import com.onixbyte.deltaforceguide.security.provider.UsernamePasswordAuthenticationProvider;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -19,6 +21,8 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
|
|||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import com.onixbyte.deltaforceguide.exeption.BizException;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||||
import org.springframework.web.cors.CorsConfigurationSource;
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
@@ -34,6 +38,8 @@ import org.springframework.web.cors.CorsConfigurationSource;
|
|||||||
@EnableConfigurationProperties({TokenProperties.class, CookieProperties.class})
|
@EnableConfigurationProperties({TokenProperties.class, CookieProperties.class})
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(SecurityConfig.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the HTTP security filter chain including endpoint authorisation and JWT filter.
|
* Configures the HTTP security filter chain including endpoint authorisation and JWT filter.
|
||||||
*
|
*
|
||||||
@@ -55,6 +61,16 @@ public class SecurityConfig {
|
|||||||
.authorizeHttpRequests((customiser) -> customiser
|
.authorizeHttpRequests((customiser) -> customiser
|
||||||
.anyRequest().permitAll()
|
.anyRequest().permitAll()
|
||||||
)
|
)
|
||||||
|
.exceptionHandling(customiser -> customiser
|
||||||
|
.authenticationEntryPoint((request, response, authException) -> {
|
||||||
|
log.error("Unauthenticated request: {}", request, authException);
|
||||||
|
throw new BizException(HttpStatus.UNAUTHORIZED, "请先登录");
|
||||||
|
})
|
||||||
|
.accessDeniedHandler((request, response, accessDeniedException) -> {
|
||||||
|
log.error("Denied request: {}", request, accessDeniedException);
|
||||||
|
throw new BizException(HttpStatus.FORBIDDEN, "权限不足");
|
||||||
|
})
|
||||||
|
)
|
||||||
.addFilterAfter(tokenAuthenticationFilter, ExceptionTranslationFilter.class)
|
.addFilterAfter(tokenAuthenticationFilter, ExceptionTranslationFilter.class)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.onixbyte.deltaforceguide.controller;
|
|||||||
import com.onixbyte.deltaforceguide.domain.dto.GitHubIssueRequest;
|
import com.onixbyte.deltaforceguide.domain.dto.GitHubIssueRequest;
|
||||||
import com.onixbyte.deltaforceguide.service.WebhookService;
|
import com.onixbyte.deltaforceguide.service.WebhookService;
|
||||||
import com.onixbyte.deltaforceguide.shared.GitHubWebhookHeader;
|
import com.onixbyte.deltaforceguide.shared.GitHubWebhookHeader;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@@ -12,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestHeader;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@Tag(name = "GitHub WebHook")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/webhooks/github")
|
@RequestMapping("/webhooks/github")
|
||||||
public class GitHubWebhookController {
|
public class GitHubWebhookController {
|
||||||
@@ -24,6 +27,7 @@ public class GitHubWebhookController {
|
|||||||
this.webhookService = webhookService;
|
this.webhookService = webhookService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(description = "GitHub WebHook 接口")
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity<Void> handleWebhook(
|
public ResponseEntity<Void> handleWebhook(
|
||||||
@RequestHeader(GitHubWebhookHeader.EVENT) String event,
|
@RequestHeader(GitHubWebhookHeader.EVENT) String event,
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ package com.onixbyte.deltaforceguide.controller;
|
|||||||
|
|
||||||
import com.onixbyte.deltaforceguide.domain.dto.ErrorResponse;
|
import com.onixbyte.deltaforceguide.domain.dto.ErrorResponse;
|
||||||
import com.onixbyte.deltaforceguide.exeption.BizException;
|
import com.onixbyte.deltaforceguide.exeption.BizException;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
@@ -20,5 +24,22 @@ public class GlobalExceptionHandler {
|
|||||||
return ResponseEntity.status(status)
|
return ResponseEntity.status(status)
|
||||||
.body(new ErrorResponse(exception.getMessage()));
|
.body(new ErrorResponse(exception.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(AuthenticationException.class)
|
||||||
|
public ResponseEntity<ErrorResponse> handleAuthenticationException(AuthenticationException exception) {
|
||||||
|
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
|
||||||
|
.body(new ErrorResponse("请先登录"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(AccessDeniedException.class)
|
||||||
|
public ResponseEntity<ErrorResponse> handleAccessDeniedException(AccessDeniedException exception) {
|
||||||
|
var authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
var httpStatus = authentication == null || !authentication.isAuthenticated()
|
||||||
|
? HttpStatus.UNAUTHORIZED
|
||||||
|
: HttpStatus.FORBIDDEN;
|
||||||
|
var message = httpStatus == HttpStatus.UNAUTHORIZED ? "请先登录" : "权限不足";
|
||||||
|
return ResponseEntity.status(httpStatus)
|
||||||
|
.body(new ErrorResponse(message));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user