Compare commits
6 Commits
1.4.0-dev.1
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
0aeaac17a4
|
|||
|
a2469c7573
|
|||
|
62fcce3005
|
|||
|
c18a108a2b
|
|||
|
fb2732508a
|
|||
|
ceac8a3f8c
|
@@ -50,24 +50,36 @@ jobs:
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
username: ${{ vars.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
||||
|
||||
- name: Generate image tags
|
||||
id: meta
|
||||
run: |
|
||||
DOCKERHUB_USER="${{ secrets.DOCKER_HUB_USERNAME }}"
|
||||
REPO_NAME=$(echo "${{ gitea.repository.name }}" | tr '[:upper:]' '[:lower:]')
|
||||
echo "tag_version=${DOCKERHUB_USER}/${REPO_NAME}:${{ gitea.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
|
||||
echo "tag_latest=${DOCKERHUB_USER}/${REPO_NAME}:latest" >> "$GITHUB_OUTPUT"
|
||||
DOCKERHUB_USER="${{ vars.DOCKER_HUB_USERNAME }}"
|
||||
echo "tag_version=${DOCKERHUB_USER}/${APP_NAME}:${{ gitea.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
|
||||
echo "tag_latest=${DOCKERHUB_USER}/${APP_NAME}:latest" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
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
|
||||
tags: |
|
||||
${{ steps.meta.outputs.tag_version }}
|
||||
${{ 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
|
||||
|
||||
ARG JAR_FILE
|
||||
ARG IMAGE_VERSION=dev
|
||||
ARG IMAGE_VENDOR=OnixByte
|
||||
ARG BUILD_DATE
|
||||
|
||||
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"]
|
||||
|
||||
@@ -65,6 +65,7 @@ dependencies {
|
||||
|
||||
tasks.processResources {
|
||||
filesMatching("application.yaml") {
|
||||
println("appVersion = ${artefactVersion}, channel = ${buildChannel}, vendor = ${vendor}")
|
||||
expand(
|
||||
"appVersion" to artefactVersion,
|
||||
"channel" to buildChannel,
|
||||
|
||||
@@ -9,15 +9,19 @@ spring:
|
||||
host: localhost
|
||||
port: 6379
|
||||
database: 0
|
||||
# password: 6hLFVqfGPviTYukn # Uncomment if password is necessary
|
||||
# password: qwerty # Uncomment if password is necessary
|
||||
|
||||
logging:
|
||||
pattern:
|
||||
# dateformat: dd MMM yyyy HH:mm:ss.SSS # Modify this for custom date format.
|
||||
|
||||
app:
|
||||
common:
|
||||
version: 1.3.0.8-dev # Application version, you can change to any version you like, used for communication with frontend.
|
||||
cookie:
|
||||
http-only: true
|
||||
secure: false
|
||||
same-site: none
|
||||
path: '/'
|
||||
max-age: P1D
|
||||
cors:
|
||||
allowed-origins: # Cross-origin allowed origins
|
||||
- "http://localhost:5173" # Dev server for vite.
|
||||
@@ -41,12 +45,10 @@ app:
|
||||
issuer: dfguide.local # Issuer host
|
||||
secret: qwertyuiopasdfghjklzxcvbnm123456 # JWT singing secret, a 32-byte long or longer string is recommended
|
||||
valid-time: PT2H # JWT valid duration
|
||||
cookie: # Cookie settings.
|
||||
http-only: true
|
||||
secure: false
|
||||
same-site: lax
|
||||
path: '/'
|
||||
max-age: PT2H
|
||||
webhook:
|
||||
github:
|
||||
secret: 123456
|
||||
allowed-users: [ octotcat, onixbyte ]
|
||||
|
||||
springdoc:
|
||||
api-docs:
|
||||
|
||||
@@ -10,14 +10,14 @@ import org.springframework.context.annotation.Configuration;
|
||||
info = @Info(
|
||||
title = "Delta Force Guide Server",
|
||||
description = "API for managing Delta Force game firearm builds",
|
||||
version = "1.3.4",
|
||||
version = "1.4.0",
|
||||
contact = @Contact(
|
||||
name = "Zihlu Wang",
|
||||
email = "zihlu.wang@onixbyte.com"
|
||||
),
|
||||
license = @License(
|
||||
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.TokenProperties;
|
||||
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.context.annotation.Bean;
|
||||
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.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
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.access.ExceptionTranslationFilter;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
@@ -34,6 +38,8 @@ import org.springframework.web.cors.CorsConfigurationSource;
|
||||
@EnableConfigurationProperties({TokenProperties.class, CookieProperties.class})
|
||||
public class SecurityConfig {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SecurityConfig.class);
|
||||
|
||||
/**
|
||||
* Configures the HTTP security filter chain including endpoint authorisation and JWT filter.
|
||||
*
|
||||
@@ -55,6 +61,16 @@ public class SecurityConfig {
|
||||
.authorizeHttpRequests((customiser) -> customiser
|
||||
.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)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.onixbyte.deltaforceguide.controller;
|
||||
import com.onixbyte.deltaforceguide.domain.dto.GitHubIssueRequest;
|
||||
import com.onixbyte.deltaforceguide.service.WebhookService;
|
||||
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.LoggerFactory;
|
||||
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.RestController;
|
||||
|
||||
@Tag(name = "GitHub WebHook")
|
||||
@RestController
|
||||
@RequestMapping("/webhooks/github")
|
||||
public class GitHubWebhookController {
|
||||
@@ -24,6 +27,7 @@ public class GitHubWebhookController {
|
||||
this.webhookService = webhookService;
|
||||
}
|
||||
|
||||
@Operation(description = "GitHub WebHook 接口")
|
||||
@PostMapping
|
||||
public ResponseEntity<Void> handleWebhook(
|
||||
@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.exeption.BizException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
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.RestControllerAdvice;
|
||||
|
||||
@@ -20,5 +24,22 @@ public class GlobalExceptionHandler {
|
||||
return ResponseEntity.status(status)
|
||||
.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