11 Commits
main ... v0.0.1

Author SHA1 Message Date
39e7c72527 chore: update and move to the new server
All checks were successful
Docker Image CI / build (push) Successful in 9m1s
2026-03-06 10:34:38 +08:00
ccaddf7df7 chore: update and move to the new server 2026-03-06 10:15:06 +08:00
merlin
ff49b4f508 fix: ci 2026-02-04 18:05:20 +08:00
merlin
38db950731 feat: add upload img logic 2025-11-06 11:09:45 +08:00
merlin
c3787aab2e fix(Dockerfile): remove debug tools 2025-11-05 16:31:43 +08:00
merlin
b65174e065 fix(Dockerfile): add debug tools 2025-11-05 15:11:41 +08:00
merlin
ecdb6537c5 fix: Dockerfile build order 2025-11-05 09:38:45 +08:00
merlin
f1968590a9 feat: add dicker ignore; add search logic 2025-11-05 09:36:08 +08:00
merlin
6d6fe921fc fix(Dockerfile): fix command mkdir 2025-11-02 11:34:35 +08:00
merlin
1a7fb6136b fix(Dockerfile): fix command mkdir 2025-11-02 11:32:55 +08:00
merlin
093c2cfe1d fix(ci): fix builder image name 2025-11-02 01:15:02 +08:00
14 changed files with 190 additions and 158 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
src/main/resources/application.yml

View File

@@ -6,9 +6,9 @@ on:
jobs:
build:
runs-on: gitea-official-runner
runs-on: gitea-runner-group-blog
container:
image: harbor.merlin.xin/release/merlin/builder:v0.0.1
image: ${{ vars.HARBOR_URL }}/candlelight/action_builder:v0.0.1
steps:
- name: Checkout code
uses: actions/checkout@v4

View File

@@ -6,9 +6,9 @@ on:
jobs:
build:
runs-on: gitea-official-runner
runs-on: gitea-runner-group-blog
container:
image: harbor.merlin.xin/release/merlin/builder:v0.0.1
image: ${{ vars.HARBOR_URL }}/candlelight/action_builder:v0.0.1
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -24,7 +24,7 @@ jobs:
REPOSITORY: ${{ github.repository }}
run: |
ROOT_DIR=$(pwd)
IMAGE_NAME="${HARBOR_URL}/release/$REPOSITORY:$GITHUB_REF_NAME"
IMAGE_NAME="${HARBOR_URL}/$REPOSITORY:$GITHUB_REF_NAME"
echo "Building image: ${IMAGE_NAME}"
docker build -t ${IMAGE_NAME} .
echo "Pushing image: ${IMAGE_NAME}"

View File

@@ -1,5 +1,5 @@
# ===== build stage =====
FROM harbor.merlin.xin/mirrors/docker.io/library/maven:3.9.6-eclipse-temurin-17 AS builder
FROM registry.merlin.xin/library/maven:4.0.0-rc-5-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
@@ -10,15 +10,25 @@ RUN --mount=type=cache,target=/root/.m2 mvn -B -q package -DskipTests
# ===== runtime stage =====
FROM harbor.merlin.xin/mirrors/docker.io/library/eclipse-temurin:17-jre-alpine
FROM registry.merlin.xin/library/eclipse-temurin:17-jre-alpine
# >>> Install debug tools <<<
#RUN apk update && apk add --no-cache \
# curl \
# bind-tools \
# busybox-extras \
# iproute2 \
# tcpdump \
# net-tools
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
RUN mkdir uploads/photo
# 非 root 用户运行
RUN addgroup -S spring && adduser -S spring -G spring
RUN mkdir -p /app/uploads/photo \
&& chown -R spring:spring /app/uploads
USER spring
ENTRYPOINT ["java","-jar","/app/app.jar"]
ENTRYPOINT ["java","-jar","/app/app.jar","--spring.config.location=file:/app/application.yml"]

View File

@@ -87,6 +87,14 @@
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>application.yml</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>

View File

@@ -30,7 +30,7 @@ public class SecurityConfig {
.requestMatchers(
"/login",
"/register",
"/test",
"/health",
"/code/**",
"/blog/**"
).permitAll()

View File

@@ -3,7 +3,9 @@ package xin.merlin.myblog_server.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import xin.merlin.myblog_server.entity.Article;
import xin.merlin.myblog_server.entity.Comment;
import xin.merlin.myblog_server.entity.News;
@@ -15,6 +17,14 @@ import xin.merlin.myblog_server.utils.JwtUtil;
import xin.merlin.myblog_server.utils.RequestBack;
import xin.merlin.myblog_server.utils.enums.ResultCode;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.UUID;
@RestController
@RequestMapping("/admin")
public class AdminController {
@@ -32,6 +42,9 @@ public class AdminController {
@Autowired
private CommentServiceImpl commentService;
@Value("${upload.dir}")
private String uploadDir;
//编辑,新增,删除新闻
@PostMapping("/update/news")
RequestBack editNews(@RequestBody News news, @RequestHeader("Authorization")String token) {
@@ -100,4 +113,30 @@ public class AdminController {
commentService.removeById(user.getId());
return RequestBack.success(ResultCode.SUCCESS);
}
//上传照片
@PostMapping("/upload/img")
RequestBack uploadImg(@RequestHeader("Authorization")String token, @RequestParam("image") MultipartFile file) throws IOException {
if(!jwtUtil.getUAccount(token.substring(7)).equals("admin")) return RequestBack.fail(ResultCode.USER_NOT_FOUND);
if (file == null || file.isEmpty()) {
return RequestBack.fail(ResultCode.NOT_FOUND);
}
String original = file.getOriginalFilename();
String ext = "";
if (original != null && original.contains(".")) {
ext = original.substring(original.lastIndexOf('.'));
}
String filename = UUID.randomUUID().toString() + ext;
Path dirPath = Paths.get(uploadDir);
Files.createDirectories(dirPath);
Path target = dirPath.resolve(filename);
try (InputStream in = file.getInputStream()) {
Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
}
return RequestBack.success(ResultCode.SUCCESS, "https://blog.merlin.xin/app/uploads/"+filename);
}
}

View File

@@ -1,6 +1,9 @@
package xin.merlin.myblog_server.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
@@ -13,6 +16,8 @@ import xin.merlin.myblog_server.utils.JwtUtil;
import xin.merlin.myblog_server.utils.RequestBack;
import xin.merlin.myblog_server.utils.enums.ResultCode;
import java.util.List;
@RestController
@RequestMapping("/blog")
public class BasicController {
@@ -43,8 +48,24 @@ public class BasicController {
@GetMapping("/get/news")
public RequestBack getNews(@RequestParam Integer current,@RequestParam Integer size) {
Page<News> page = new Page<>(current,size);
page.setOrders(List.of(OrderItem.desc("published")));
return RequestBack.success(ResultCode.SUCCESS,newsService.page(page));
}
// 搜索新闻
@PostMapping("/search/news")
public RequestBack searchNews(@RequestParam Integer current,
@RequestParam Integer size,
@RequestBody News news) {
Page<News> page = new Page<>(current, size);
QueryWrapper<News> qw = new QueryWrapper<>();
qw.like(news.getTitle() != null, "title", news.getTitle())
.orderByDesc("published");
IPage<News> result = newsService.page(page, qw);
return RequestBack.success(ResultCode.SUCCESS, result);
}
// 获取文章
@GetMapping("/get/article/{a_id}")
public RequestBack getArticle(@PathVariable Integer a_id) {
@@ -54,6 +75,7 @@ public class BasicController {
@GetMapping("/get/articles")
public RequestBack getArticles(@RequestParam Integer current,@RequestParam Integer size) {
Page<Article> page = new Page<>(current,size);
page.setOrders(List.of(OrderItem.desc("published")));
return RequestBack.success(ResultCode.SUCCESS,articleService.page(page));
}
// 发表评论
@@ -62,5 +84,20 @@ public class BasicController {
commentService.save(comment);
return RequestBack.success(ResultCode.SUCCESS);
}
// 搜索新闻
@PostMapping("/search/articles")
public RequestBack searchArticles(@RequestParam Integer current,
@RequestParam Integer size,
@RequestBody Article article) {
Page<Article> page = new Page<>(current, size);
QueryWrapper<Article> qw = new QueryWrapper<>();
qw.like(article.getTitle() != null, "title", article.getTitle())
.orderByDesc("published");
IPage<Article> result = articleService.page(page, qw);
return RequestBack.success(ResultCode.SUCCESS, result);
}
// 联系管理员
}

View File

@@ -1,7 +1,5 @@
package xin.merlin.myblog_server.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.benmanes.caffeine.cache.Cache;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
@@ -17,7 +15,6 @@ import xin.merlin.myblog_server.service.LoginService;
import xin.merlin.myblog_server.service.impl.UserServiceImpl;
import xin.merlin.myblog_server.utils.JwtUtil;
import xin.merlin.myblog_server.utils.RequestBack;
import xin.merlin.myblog_server.utils.SHA256Util;
import xin.merlin.myblog_server.utils.enums.ResultCode;
import java.util.Map;
@@ -31,8 +28,6 @@ public class LoginController {
@Autowired
private UserServiceImpl userServiceImpl;
@Autowired
private SHA256Util sha256Util;
@Autowired
private JwtUtil jwtUtil;

View File

@@ -1,19 +1,12 @@
package xin.merlin.myblog_server.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import xin.merlin.myblog_server.utils.JwtUtil;
@RestController
public class TestController {
@Autowired
private JwtUtil jwtUtil;
@GetMapping("/test")
@GetMapping("/health")
public String test() {
String token = jwtUtil.generateToken("1223",12);
return token;
return "ok";
}
}

View File

@@ -3,7 +3,9 @@ package xin.merlin.myblog_server.utils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import xin.merlin.myblog_server.config.JwtProperties;
@@ -12,6 +14,8 @@ import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.UUID;
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtUtil {
@@ -34,6 +38,16 @@ public class JwtUtil {
}
}
@PreDestroy
public void destroy() {
log.info("JWT 组件正在关闭...");
// 如有线程池、定时任务、IO 连接,在这里 shutdown/close
// 本例仅把引用置空,帮助 GC可选
this.jwtParser = null;
this.key = null;
log.info("JWT 组件已关闭");
}
/**
* 生成 JWT Token
*/

View File

@@ -1,53 +0,0 @@
package xin.merlin.myblog_server.utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@Component
public class SHA256Util {
/**
* 对输入字符串进行SHA-256加密
* @param input 输入字符串
* @return 加密后的十六进制字符串
*/
public String encryptSHA256(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] encodedhash = digest.digest(input.getBytes());
return bytesToHex(encodedhash);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-256 algorithm not found", e);
}
}
/**
* 将字节数组转换为十六进制字符串
* @param hash 字节数组
* @return 十六进制字符串
*/
private String bytesToHex(byte[] hash) {
StringBuilder hexString = new StringBuilder(2 * hash.length);
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
/**
* 使用用户ID生成盐值并对密码进行加密
* @param password 用户输入的密码
* @return 加密后的密码哈希值
*/
public String encryptPassword(String password) {
// 将盐值与密码拼接后进行SHA-256加密
return encryptSHA256( password);
}
}

View File

@@ -1,11 +1,5 @@
server:
port: 8080
# port: 8443
# ssl:
# key-store: classpath:merlin.xin.pfx
# key-store-password: 7p7vcfmu
# key-store-type: PKCS12
# address: 0.0.0.0
port: 8081
jwt:
secret: CkmEXxVBNBsMUo4VNhDcH0YBhA1O4zSkQgSM243YzDY=
@@ -13,6 +7,8 @@ jwt:
subject: Interesting
expire: 604800
upload:
dir: c:/uploads
spring:
servlet:
@@ -27,14 +23,6 @@ spring:
jackson:
time-zone: Asia/Shanghai
date-format: yyyy-MM-dd HH:mm:ss
# url: jdbc:mysql://8.138.214.149:3306/blog
# username: root
# password: 3604162
# username: root
# password: server2025_xyf_Merlin
# driver-class-name: com.mysql.cj.jdbc.Driver
mail:
protocol: smtps
port: 465

Binary file not shown.