Skip to content

Commit b4230f7

Browse files
committed
feat: AI Gateway, Client Certificate (mTLS), API management now supports setting CORS headers, gRPC test connections, automatic trust certificate functionality for gRPC proxy, and optimized API exception report UI.
1 parent 87323f5 commit b4230f7

File tree

340 files changed

+26603
-13210
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

340 files changed

+26603
-13210
lines changed

build.gradle

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ licenseReport {
3535

3636
allprojects {
3737
group = 'tpi.dgrv4'
38-
version = 'release-v4.5.16'
38+
version = 'release-rc-v4.5.19.0-4-g052aa0662'
3939
apply plugin: 'io.spring.dependency-management'
4040

4141
repositories {
@@ -62,8 +62,8 @@ subprojects {
6262

6363

6464
dependencies {
65-
implementation files("${rootDir}/libsext/dgrv4_CodecUtil-v4.5.16.3-lib.jar")
66-
implementation files("${rootDir}/libsext/dgrv4_HttpUtil-v4.5.16.3-lib.jar")
65+
implementation files("${rootDir}/libsext/dgrv4_CodecUtil-v4.5.19.0-4-g052aa0662-lib.jar")
66+
implementation files("${rootDir}/libsext/dgrv4_HttpUtil-v4.5.19.0-4-g052aa0662-lib.jar")
6767

6868
implementation ('org.springframework.boot:spring-boot-starter-data-jpa'){
6969
}
@@ -87,6 +87,8 @@ subprojects {
8787
implementation 'commons-io:commons-io:2.16.1'
8888
// CVE-2025-8885
8989
implementation 'org.bouncycastle:bc-fips:2.1.1'
90+
91+
implementation 'org.bouncycastle:bcpkix-fips:2.1.9'
9092
}
9193

9294
// clear lib
@@ -249,6 +251,8 @@ project(':dgrv4_Gateway_serv'){
249251
implementation 'io.netty:netty-codec:4.1.125.Final'
250252

251253
implementation 'io.netty:netty-tcnative-boringssl-static:2.0.61.Final'
254+
255+
implementation 'com.google.code.gson:gson:2.10.1'
252256
}
253257

254258
protobuf {

dgrv4_Common_lib/src/main/java/tpi/dgrv4/common/constant/TsmpDpAaRtnCode.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,8 @@ public enum TsmpDpAaRtnCode implements ITsmpDpAaError<TsmpDpAaException> {
426426
_1557(TsmpDpModule.DP5, "57", "啟用日期需要小於停用日期"),
427427
_1558(TsmpDpModule.DP5, "58", "停用日期需要小於啟用日期"),
428428
_1559(TsmpDpModule.DP5, "59", "{{0}}"),
429+
_1560(TsmpDpModule.DP5, "60", "憑證過期"),
430+
429431
_1561(TsmpDpModule.DP5, "61", "對稱式加密失敗:{{0}}"),
430432
_1562(TsmpDpModule.DP5, "62", "對稱式解密失敗:{{0}}"),
431433

@@ -468,6 +470,7 @@ public enum TsmpDpAaRtnCode implements ITsmpDpAaError<TsmpDpAaException> {
468470
_2036(TsmpDpModule.DP10, "36", "必須先移除預定下架日期"),
469471
_2037(TsmpDpModule.DP10, "37", "上架日期需要小於下架日期"),
470472
_2038(TsmpDpModule.DP10, "38", "下架日期需要小於上架日期"),
473+
_2039(TsmpDpModule.DP10, "39", "啟用中,無法刪除"),
471474
;
472475

473476
private TsmpDpModule module;
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package tpi.dgrv4.common.utils.autoInitSQL.Initializer;
2+
3+
import lombok.Builder;
4+
import org.springframework.stereotype.Service;
5+
6+
import java.util.List;
7+
8+
@Service
9+
public class AiGatewayTableInitializer {
10+
11+
@Builder
12+
public record AiEngineProvider(
13+
Long id,
14+
String name,
15+
String alias,
16+
String model,
17+
String generateAPI,
18+
String countTokenAPI
19+
) {}
20+
21+
@Builder
22+
public record AiPromptTemplate(
23+
Long id,
24+
String name,
25+
String content,
26+
String remark
27+
) {}
28+
29+
public List<AiEngineProvider> defaultProviders() {
30+
var google = AiEngineProvider.builder()
31+
.name("Google");
32+
var gemini_2_5_FlashLite = google
33+
.id(1L)
34+
.alias("Gemini 2.5 Flash Lite")
35+
.model("gemini-2.5-flash-lite")
36+
.generateAPI("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-lite:generateContent")
37+
.countTokenAPI("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-lite:countTokens")
38+
.build();
39+
40+
var gemini_2_5_Flash= google
41+
.id(2L)
42+
.alias("Gemini 2.5 Flash")
43+
.model("gemini-2.5-flash")
44+
.generateAPI("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent")
45+
.countTokenAPI("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:countTokens")
46+
.build();
47+
48+
var openai = AiEngineProvider.builder()
49+
.name("OpenAI");
50+
51+
var gpt_4oMini = openai
52+
.id(3L)
53+
.alias("OpenAI GPT 4o Mini")
54+
.model("gpt-4o-mini")
55+
.generateAPI("https://api.openai.com/v1/responses")
56+
.countTokenAPI("#")
57+
.build();
58+
59+
var gpt_o1Mini = openai
60+
.id(4L)
61+
.alias("OpenAI GPT o1 Mini")
62+
.model("o1-mini")
63+
.generateAPI("https://api.openai.com/v1/responses")
64+
.countTokenAPI("#")
65+
.build();
66+
return List.of(
67+
gemini_2_5_FlashLite
68+
,gemini_2_5_Flash
69+
// ,gpt_4oMini
70+
// ,gpt_o1Mini
71+
);
72+
}
73+
74+
public List<AiPromptTemplate> defaultTemplates() {
75+
var defaultTemplate = AiPromptTemplate.builder()
76+
.id(1L)
77+
.name("Default")
78+
.content("")
79+
.remark("ai prompt default template")
80+
.build();
81+
return List.of(defaultTemplate);
82+
}
83+
}

dgrv4_Common_lib/src/main/java/tpi/dgrv4/common/utils/autoInitSQL/Initializer/TsmpFuncTableInitializer.java

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.List;
1212
import java.util.stream.Collectors;
1313

14+
@SuppressWarnings("java:S1192")
1415
@Service
1516
public class TsmpFuncTableInitializer {
1617

@@ -43,7 +44,9 @@ public class TsmpFuncTableInitializer {
4344
//系統現況查詢
4445
"NP05","NP0504","NP0512","NP0513","NP0514","NP0516","NP1201","NP1202",
4546
//系統設定
46-
"LB00","LB0001","LB0002","LB0003","LB0004","LB0005","LB0006","LB0007","LB0008","LB0009","LB0010","LB0011","LB0012"
47+
"LB00","LB0001","LB0002","LB0003","LB0004","LB0005","LB0006","LB0007","LB0008","LB0009","LB0010","LB0011","LB0012",
48+
// AI Gateway
49+
"AI00","AI0001","AI0002","AI0003","AI0004","AI0005",
4750
};
4851
public static final String[] ENTERPRISE = new String[] {
4952
//v4-權限管理
@@ -65,7 +68,9 @@ public class TsmpFuncTableInitializer {
6568
//系統現況查詢
6669
"NP05","NP0504","NP0513","NP0514","NP0516","NP1201","NP1202",
6770
//系統設定
68-
"LB00","LB0001","LB0002","LB0003","LB0004","LB0005","LB0006","LB0007","LB0008","LB0009","LB0010","LB0011","LB0012"
71+
"LB00","LB0001","LB0002","LB0003","LB0004","LB0005","LB0006","LB0007","LB0008","LB0009","LB0010","LB0011","LB0012",
72+
// AI Gateway
73+
"AI00","AI0001","AI0002","AI0003","AI0004","AI0005",
6974
};
7075
public static final String[] ENTERPRISE_LITE = new String[] {
7176
//v4-權限管理
@@ -82,13 +87,15 @@ public class TsmpFuncTableInitializer {
8287
//20250407 no execute
8388
//"AC13","AC1301","AC1302","AC1303","AC1304","AC1305",
8489
//用戶憑證管理
85-
"NP02","NP0201","NP0202","NP0203","NP0204",
90+
"NP02","NP0201","NP0202","NP0203","NP0204","NP0205",
8691
//各類申請單
8792
"NP03","NP0304","NP0401","NP0402",
8893
//系統現況查詢
8994
"NP05","NP0504","NP0513","NP0514","NP0516","NP1201","NP1202",
9095
//系統設定
91-
"LB00","LB0001","LB0002","LB0003","LB0004","LB0005","LB0006","LB0007","LB0008","LB0009","LB0010","LB0011","LB0012"
96+
"LB00","LB0001","LB0002","LB0003","LB0004","LB0005","LB0006","LB0007","LB0008","LB0009","LB0010","LB0011","LB0012",
97+
// AI Gateway
98+
"AI00","AI0001","AI0002","AI0003","AI0004","AI0005",
9299
};
93100
public static final String[] EXPRESS = new String[] {
94101
//v4-權限管理
@@ -105,13 +112,15 @@ public class TsmpFuncTableInitializer {
105112
//20250407 no execute
106113
//"AC13","AC1301","AC1302","AC1303","AC1304","AC1305",
107114
//用戶憑證管理
108-
"NP02","NP0201","NP0202","NP0203","NP0204",
115+
"NP02","NP0201","NP0202","NP0203","NP0204","NP0205",
109116
//各類申請單
110117
"NP03","NP0304","NP0401","NP0402",
111118
//系統現況查詢
112119
"NP05","NP0504","NP0513","NP0514","NP0516","NP1201","NP1202",
113120
//系統設定
114-
"LB00","LB0001","LB0002","LB0003","LB0004","LB0005","LB0006","LB0007","LB0008","LB0009","LB0010","LB0011","LB0012"
121+
"LB00","LB0001","LB0002","LB0003","LB0004","LB0005","LB0006","LB0007","LB0008","LB0009","LB0010","LB0011","LB0012",
122+
// AI Gateway
123+
"AI00","AI0001","AI0002","AI0003","AI0004","AI0005",
115124
};
116125

117126
public List<TsmpFuncVo> insertTsmpFunc(LicenseEditionTypeVo licenseEdition, boolean isOsType) {
@@ -172,8 +181,8 @@ public List<TsmpFuncVo> insertTsmpFunc(LicenseEditionTypeVo licenseEdition, bool
172181
createTsmpFunc("AC0501","digiRunner監控","digiRunnerMonitor","digiRunner Server監控",null,"zh-TW","manager",DateTimeUtil.now());
173182
createTsmpFunc("AC0502","System Resource","","digiRunnerDashboard",null,"en-US","manager",DateTimeUtil.now());
174183
createTsmpFunc("AC0502","digiRunner儀表板","digiRunnerDashboard","digiRunner儀表板",null,"zh-TW","manager",DateTimeUtil.now());
175-
createTsmpFunc("AC0503","API Abnormal Report","API Abnormal Report","API Abnormal Report","","en-US","manager",DateTimeUtil.now());
176-
createTsmpFunc("AC0503","API異常報表","API Abnormal Report","API異常報表","","zh-TW","manager",DateTimeUtil.now());
184+
createTsmpFunc("AC0503","API Abnormal Real-time Information","API Abnormal Real-time Information","API Abnormal Real-time Information","","en-US","manager",DateTimeUtil.now());
185+
createTsmpFunc("AC0503","API異常即時資訊","API Abnormal Real-time Information","API異常即時資訊","","zh-TW","manager",DateTimeUtil.now());
177186
createTsmpFunc("AC0508","Elastic Stack","","Monitor and explore system data effortlessly using interactive ELK dashboards powered by Elastic Stack.",null,"en-US","manager",DateTimeUtil.now());
178187
createTsmpFunc("AC0508","ELK儀表板","","",null,"zh-TW","manager",DateTimeUtil.now());
179188

@@ -329,6 +338,19 @@ public List<TsmpFuncVo> insertTsmpFunc(LicenseEditionTypeVo licenseEdition, bool
329338

330339
createTsmpFunc("LB0010","Bot Detection","Bot Detection","botDetection","","en-US","manager",DateTimeUtil.now());
331340
createTsmpFunc("LB0010","Bot Detection","Bot Detection","botDetection","","zh-TW","manager",DateTimeUtil.now());
341+
342+
createTsmpFunc("AI00","AI Gateway","AI Gateway","aiGateway","","en-US","manager",DateTimeUtil.now());
343+
createTsmpFunc("AI00","AI Gateway","AI Gateway","aiGateway","","zh-TW","manager",DateTimeUtil.now());
344+
createTsmpFunc("AI0001","AI Provider","AI Provider","aiProvider","","en-US","manager",DateTimeUtil.now());
345+
createTsmpFunc("AI0001","AI 供應商","AI 供應商","aiProvider","","zh-TW","manager",DateTimeUtil.now());
346+
createTsmpFunc("AI0002","AI APIKEY","AI APIKEY","aiAPIKEY","","en-US","manager",DateTimeUtil.now());
347+
createTsmpFunc("AI0002","AI APIKEY","AI APIKEY","aiAPIKEY","","zh-TW","manager",DateTimeUtil.now());
348+
createTsmpFunc("AI0003","AI APIKEY USAGE","AI APIKEY USAGE","aiAPIKEYUsage","","en-US","manager",DateTimeUtil.now());
349+
createTsmpFunc("AI0003","AI APIKEY 使用情況","AI APIKEY 使用情況","aiAPIKEYUsage","","zh-TW","manager",DateTimeUtil.now());
350+
createTsmpFunc("AI0004","AI Prompt Template","AI Prompt Template","aiPromptTemplate","","en-US","manager",DateTimeUtil.now());
351+
createTsmpFunc("AI0004","AI 提示詞樣版","AI 提示詞樣版","aiPromptTemplate","","zh-TW","manager",DateTimeUtil.now());
352+
createTsmpFunc("AI0005","User AI Prompt Template","User AI Prompt Template","userAiPromptTemplateSetting","","en-US","manager",DateTimeUtil.now());
353+
createTsmpFunc("AI0005","使用者 AI 提示詞樣版設定","使用者預設 AI 提示詞樣版設定","userAiPromptTemplateSetting","","zh-TW","manager",DateTimeUtil.now());
332354

333355
createTsmpFunc("LB0011","Webhook","Webhook","Real time event notification mechanism","","en-US","manager",DateTimeUtil.now());
334356
createTsmpFunc("LB0011","Webhook","Webhook","即時事件通知推送機制","","zh-TW","manager",DateTimeUtil.now());

dgrv4_Common_lib/src/main/java/tpi/dgrv4/common/utils/autoInitSQL/Initializer/TsmpRtnCodeTableInitializer.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,13 +1041,18 @@ public List<TsmpRtnCodeVo> insertTsmpRtnCode() {
10411041
// 20240905, 自訂錯誤訊息, Mini Lee
10421042
createTsmpRtnCode((tsmpRtnCodeColumn = "1559"), (locale = "zh-TW"), (tsmpRtnMsg = "{{0}}"), (tsmpRtnDesc = ""));
10431043
createTsmpRtnCode((tsmpRtnCodeColumn = "1559"), (locale = "en-US"), (tsmpRtnMsg = "{{0}}"), (tsmpRtnDesc = ""));
1044+
10441045

10451046
// 20250206, 對稱式加密/解密失敗訊息, Mini Lee
10461047
createTsmpRtnCode((tsmpRtnCodeColumn = "1561"), (locale = "zh-TW"), (tsmpRtnMsg = "對稱式加密失敗:{{0}}"), (tsmpRtnDesc = ""));
10471048
createTsmpRtnCode((tsmpRtnCodeColumn = "1561"), (locale = "en-US"), (tsmpRtnMsg = "Symmetric encryption error: {{0}}"), (tsmpRtnDesc = ""));
10481049

10491050
createTsmpRtnCode((tsmpRtnCodeColumn = "1562"), (locale = "zh-TW"), (tsmpRtnMsg = "對稱式解密失敗:{{0}}"), (tsmpRtnDesc = ""));
10501051
createTsmpRtnCode((tsmpRtnCodeColumn = "1562"), (locale = "en-US"), (tsmpRtnMsg = "Symmetric decryption error: {{0}}"), (tsmpRtnDesc = ""));
1052+
// 20250116, 刪除失敗,狀態為啟用中, Zoe Lee
1053+
createTsmpRtnCode((tsmpRtnCodeColumn = "2039"), (locale = "zh-TW"), (tsmpRtnMsg = "啟用中,無法刪除"), (tsmpRtnDesc = ""));
1054+
createTsmpRtnCode((tsmpRtnCodeColumn = "2039"), (locale = "en-US"), (tsmpRtnMsg = "Enabled, cannot be deleted."), (tsmpRtnDesc = ""));
1055+
10511056

10521057
// 20250701, 查無簽核關卡設定訊息, Kevin Cheng
10531058
createTsmpRtnCode((tsmpRtnCodeColumn = "1565"), (locale = "zh-TW"), (tsmpRtnMsg = "查無簽核關卡設定,應設定簽核關卡"), (tsmpRtnDesc = ""));
@@ -1056,9 +1061,12 @@ public List<TsmpRtnCodeVo> insertTsmpRtnCode() {
10561061
// 20250722, 結束日期不可小於開始日期, Kevin Cheng
10571062
createTsmpRtnCode((tsmpRtnCodeColumn = "1566"), (locale = "zh-TW"), (tsmpRtnMsg = "結束日期不可小於開始日期"), (tsmpRtnDesc = ""));
10581063
createTsmpRtnCode((tsmpRtnCodeColumn = "1566"), (locale = "en-US"), (tsmpRtnMsg = "End date cannot be less than start date"), (tsmpRtnDesc = ""));
1059-
1060-
1061-
1064+
1065+
// 20250723, 憑證過期, Zoe Lee
1066+
createTsmpRtnCode((tsmpRtnCodeColumn = "1560"), (locale = "zh-TW"), (tsmpRtnMsg = "憑證過期"), (tsmpRtnDesc = ""));
1067+
createTsmpRtnCode((tsmpRtnCodeColumn = "1560"), (locale = "en-US"), (tsmpRtnMsg = "The Certificate has expired."), (tsmpRtnDesc = ""));
1068+
1069+
10621070
} catch (Exception e) {
10631071
StackTraceUtil.logStackTrace(e);
10641072
throw e;

dgrv4_Common_lib/src/main/java/tpi/dgrv4/common/utils/autoInitSQL/Initializer/TsmpSettingTableInitializer.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,11 @@ public List<TsmpSettingVo> insertTsmpSetting() {
305305
// -- 2025/06/10 Audit log retention days, Kevin Cheng
306306
createTsmpSetting((id = "AUDIT_LOG_RETENTION_DAYS"), (value = "0"), (memo = "Audit log retention days, default: 0, setting to 0 means using gov_long time setting"));
307307

308+
// --2025/07/31 kibana allowlist ,Zoe Lee
309+
createTsmpSetting((id = "KIBANA_REFERER_ALLOWLIST"),(value = "/app/dashboards,/app/discover,/app/monitoring"),(memo = "Allowed Referer list for Kibana. Multiple paths should be separated by \",\"."));
310+
311+
312+
308313
} catch (Exception e) {
309314
StackTraceUtil.logStackTrace(e);
310315
throw e;
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package tpi.dgrv4.entity.entity;
2+
3+
4+
5+
import com.fasterxml.jackson.annotation.JsonIgnore;
6+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
7+
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
8+
import jakarta.persistence.*;
9+
import lombok.*;
10+
import lombok.experimental.Accessors;
11+
import org.hibernate.annotations.OnDelete;
12+
import org.hibernate.annotations.OnDeleteAction;
13+
import tpi.dgrv4.codec.constant.RandomLongTypeEnum;
14+
import tpi.dgrv4.common.utils.DateTimeUtil;
15+
import tpi.dgrv4.entity.component.dgrSeq.DgrSeq;
16+
17+
import java.util.Date;
18+
import java.util.List;
19+
20+
@Builder
21+
@Data
22+
@Entity
23+
@Table(name = "dgr_ai_apikey")
24+
@NoArgsConstructor
25+
@AllArgsConstructor
26+
@Accessors(chain = true)
27+
public class DgrAiApiKey implements DgrSequenced {
28+
29+
@JsonSerialize(using = ToStringSerializer.class)
30+
@Id
31+
@DgrSeq(strategy = RandomLongTypeEnum.YYYYMMDD)
32+
@Column(name = "ai_apikey_id")
33+
private Long id;
34+
35+
@Column(name = "ai_apikey_name")
36+
private String aiApikeyName;
37+
38+
39+
@ManyToOne
40+
@JoinColumn(name = "ai_provider_id", referencedColumnName = "ai_provider_id")
41+
private DgrAiProvider dgrAiProvider; // 多個 api key 可能屬於一個 provider
42+
43+
@JsonIgnore
44+
@ToString.Exclude
45+
@OneToMany(mappedBy = "dgrAiApiKey", fetch = FetchType.EAGER)
46+
private List<DgrAiApiKeyUsage> dgrAiApiKeyUsages; // 1 個 key 對應多個 usage
47+
48+
@Column(name = "ai_apikey_code")
49+
private String aiApikeyCode;
50+
51+
@JsonSerialize(using = ToStringSerializer.class)
52+
@Builder.Default
53+
@Column(name = "usage_limit_input_token")
54+
private Long usageLimitInputToken = 0L;
55+
56+
@JsonSerialize(using = ToStringSerializer.class)
57+
@Builder.Default
58+
@Column(name = "usage_limit_output_token")
59+
private Long usageLimitOutputToken = 0L;
60+
61+
@Column(name = "usage_limit_policy")
62+
private String usageLimitPolicy;
63+
64+
@JsonSerialize(using = ToStringSerializer.class)
65+
@Builder.Default
66+
@Column(name = "usage_input_token_count")
67+
private Long usageInputTokenCount = 0L;
68+
69+
@JsonSerialize(using = ToStringSerializer.class)
70+
@Builder.Default
71+
@Column(name = "usage_output_token_count")
72+
private Long usageOutputTokenCount = 0L;
73+
74+
// @Column(name = "backup_key_id")
75+
// private Long backupKeyId;
76+
77+
@Column(name = "ai_apikey_enable")
78+
private String aiApikeyEnable;
79+
80+
@Builder.Default
81+
@Column(name = "create_date_time")
82+
private Date createDateTime = DateTimeUtil.now();
83+
84+
@Builder.Default
85+
@Column(name = "create_user")
86+
private String createUser = "SYSTEM";
87+
88+
@Column(name = "update_date_time")
89+
private Date updateDateTime;
90+
91+
@Column(name = "update_user")
92+
private String updateUser;
93+
94+
@JsonSerialize(using = ToStringSerializer.class)
95+
@Builder.Default
96+
@Column(name = "version")
97+
private Long version = 1L;
98+
99+
@Override
100+
public Long getPrimaryKey() {
101+
return id;
102+
}
103+
}

0 commit comments

Comments
 (0)