From 1d2d1bb12a9e2ea4ea4a56131e12b1d22d5e8c57 Mon Sep 17 00:00:00 2001 From: kangcheolung Date: Thu, 9 Jul 2026 14:56:59 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20McpAccessToken=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EB=B0=8F=20Flyway=20=EB=A7=88=EC=9D=B4=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- .../domain/user/entity/McpAccessToken.java | 70 +++++++++++++++++++ .../V26__create_mcp_access_tokens.sql | 11 +++ 2 files changed, 81 insertions(+) create mode 100644 src/main/java/com/opensource/docgrid/domain/user/entity/McpAccessToken.java create mode 100644 src/main/resources/db/migration/V26__create_mcp_access_tokens.sql diff --git a/src/main/java/com/opensource/docgrid/domain/user/entity/McpAccessToken.java b/src/main/java/com/opensource/docgrid/domain/user/entity/McpAccessToken.java new file mode 100644 index 00000000..cabee3ca --- /dev/null +++ b/src/main/java/com/opensource/docgrid/domain/user/entity/McpAccessToken.java @@ -0,0 +1,70 @@ +package com.opensource.docgrid.domain.user.entity; + +import java.time.LocalDateTime; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table( + name = "mcp_access_tokens", + indexes = { + @Index(name = "idx_mcp_access_tokens_user_id", columnList = "user_id") + } +) +public class McpAccessToken { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @Column(name = "token_hash", nullable = false, length = 255) + private String tokenHash; + + // BaseEntity 미사용 — updated_at 없는 스키마에 맞춰 직접 관리 + @Column(name = "created_at", nullable = false, updatable = false) + private LocalDateTime createdAt; + + @Column(name = "last_used_at") + private LocalDateTime lastUsedAt; + + @Column(name = "revoked_at") + private LocalDateTime revokedAt; + + @Builder + public McpAccessToken(User user, String tokenHash) { + this.user = user; + this.tokenHash = tokenHash; + this.createdAt = LocalDateTime.now(); + } + + public void recordUsage(LocalDateTime usedAt) { + this.lastUsedAt = usedAt; + } + + public void revoke(LocalDateTime revokedAt) { + this.revokedAt = revokedAt; + } + + public boolean isRevoked() { + return this.revokedAt != null; + } +} diff --git a/src/main/resources/db/migration/V26__create_mcp_access_tokens.sql b/src/main/resources/db/migration/V26__create_mcp_access_tokens.sql new file mode 100644 index 00000000..1f05b660 --- /dev/null +++ b/src/main/resources/db/migration/V26__create_mcp_access_tokens.sql @@ -0,0 +1,11 @@ +-- mcp_access_tokens: MCP API 접근 토큰 테이블 +CREATE TABLE mcp_access_tokens ( + id BIGSERIAL PRIMARY KEY, + user_id BIGINT NOT NULL REFERENCES users(id), + token_hash VARCHAR(255) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT now(), + last_used_at TIMESTAMP, + revoked_at TIMESTAMP +); + +CREATE INDEX idx_mcp_access_tokens_user_id ON mcp_access_tokens (user_id);