Merge branch 'mhkim-node' of http://39.117.244.52:3000/kjs/ERP-node into jskim-node
This commit is contained in:
@@ -191,18 +191,30 @@ export const getLangKeys = async (
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const { companyCode, menuCode, keyType, searchText, categoryId } = req.query;
|
||||
const userCompanyCode = req.user?.companyCode;
|
||||
logger.info("다국어 키 목록 조회 요청", {
|
||||
query: req.query,
|
||||
user: req.user,
|
||||
});
|
||||
|
||||
// company_code 필터링: 비관리자는 자기 회사 + 공통(*) 키만 조회 가능
|
||||
let effectiveCompanyCode = companyCode as string;
|
||||
if (userCompanyCode !== "*") {
|
||||
// 비관리자가 다른 회사의 데이터를 요청하면 자기 회사로 제한
|
||||
if (companyCode && companyCode !== userCompanyCode && companyCode !== "*") {
|
||||
effectiveCompanyCode = userCompanyCode || "";
|
||||
}
|
||||
}
|
||||
|
||||
const multiLangService = new MultiLangService();
|
||||
const langKeys = await multiLangService.getLangKeys({
|
||||
companyCode: companyCode as string,
|
||||
companyCode: effectiveCompanyCode,
|
||||
menuCode: menuCode as string,
|
||||
keyType: keyType as string,
|
||||
searchText: searchText as string,
|
||||
categoryId: categoryId ? parseInt(categoryId as string, 10) : undefined,
|
||||
// 비관리자: companyCode 필터가 없으면 자기 회사 + 공통(*) 키만 반환
|
||||
userCompanyCode: userCompanyCode,
|
||||
});
|
||||
|
||||
const response: ApiResponse<any[]> = {
|
||||
@@ -235,9 +247,24 @@ export const getLangTexts = async (
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const { keyId } = req.params;
|
||||
const userCompanyCode = req.user?.companyCode;
|
||||
logger.info("다국어 텍스트 조회 요청", { keyId, user: req.user });
|
||||
|
||||
const multiLangService = new MultiLangService();
|
||||
|
||||
// 비관리자: 해당 키가 자기 회사 또는 공통(*) 키인지 검증
|
||||
if (userCompanyCode !== "*") {
|
||||
const keyOwner = await multiLangService.getKeyCompanyCode(parseInt(keyId));
|
||||
if (keyOwner && keyOwner !== "*" && keyOwner !== userCompanyCode) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "다른 회사의 다국어 키에 접근할 권한이 없습니다.",
|
||||
error: { code: "PERMISSION_DENIED" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const langTexts = await multiLangService.getLangTexts(parseInt(keyId));
|
||||
|
||||
const response: ApiResponse<any[]> = {
|
||||
@@ -270,6 +297,7 @@ export const createLangKey = async (
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const keyData: CreateLangKeyRequest = req.body;
|
||||
const userCompanyCode = req.user?.companyCode;
|
||||
logger.info("다국어 키 생성 요청", { keyData, user: req.user });
|
||||
|
||||
// 필수 입력값 검증
|
||||
@@ -285,6 +313,26 @@ export const createLangKey = async (
|
||||
return;
|
||||
}
|
||||
|
||||
// 권한 검사: 공통 키(*)는 최고 관리자만 생성 가능
|
||||
if (keyData.companyCode === "*" && userCompanyCode !== "*") {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "공통 키는 최고 관리자만 생성할 수 있습니다.",
|
||||
error: { code: "PERMISSION_DENIED" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 비관리자: 자기 회사 키만 생성 가능
|
||||
if (userCompanyCode !== "*" && keyData.companyCode !== userCompanyCode) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "다른 회사의 키를 생성할 권한이 없습니다.",
|
||||
error: { code: "PERMISSION_DENIED" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const multiLangService = new MultiLangService();
|
||||
const keyId = await multiLangService.createLangKey({
|
||||
...keyData,
|
||||
@@ -323,10 +371,33 @@ export const updateLangKey = async (
|
||||
try {
|
||||
const { keyId } = req.params;
|
||||
const keyData: UpdateLangKeyRequest = req.body;
|
||||
const userCompanyCode = req.user?.companyCode;
|
||||
|
||||
logger.info("다국어 키 수정 요청", { keyId, keyData, user: req.user });
|
||||
|
||||
const multiLangService = new MultiLangService();
|
||||
|
||||
// 비관리자: 해당 키가 자기 회사 키인지 검증 (공통 키는 수정 불가)
|
||||
if (userCompanyCode !== "*") {
|
||||
const keyOwner = await multiLangService.getKeyCompanyCode(parseInt(keyId));
|
||||
if (!keyOwner) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: "다국어 키를 찾을 수 없습니다.",
|
||||
error: { code: "KEY_NOT_FOUND" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (keyOwner !== userCompanyCode) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "다른 회사의 다국어 키를 수정할 권한이 없습니다.",
|
||||
error: { code: "PERMISSION_DENIED" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await multiLangService.updateLangKey(parseInt(keyId), {
|
||||
...keyData,
|
||||
updatedBy: req.user?.userId || "system",
|
||||
@@ -362,9 +433,32 @@ export const deleteLangKey = async (
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const { keyId } = req.params;
|
||||
const userCompanyCode = req.user?.companyCode;
|
||||
logger.info("다국어 키 삭제 요청", { keyId, user: req.user });
|
||||
|
||||
const multiLangService = new MultiLangService();
|
||||
|
||||
// 비관리자: 해당 키가 자기 회사 키인지 검증 (공통 키 삭제 불가)
|
||||
if (userCompanyCode !== "*") {
|
||||
const keyOwner = await multiLangService.getKeyCompanyCode(parseInt(keyId));
|
||||
if (!keyOwner) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: "다국어 키를 찾을 수 없습니다.",
|
||||
error: { code: "KEY_NOT_FOUND" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (keyOwner !== userCompanyCode) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "다른 회사의 다국어 키를 삭제할 권한이 없습니다.",
|
||||
error: { code: "PERMISSION_DENIED" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await multiLangService.deleteLangKey(parseInt(keyId));
|
||||
|
||||
const response: ApiResponse<string> = {
|
||||
@@ -397,9 +491,32 @@ export const toggleLangKey = async (
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const { keyId } = req.params;
|
||||
const userCompanyCode = req.user?.companyCode;
|
||||
logger.info("다국어 키 상태 토글 요청", { keyId, user: req.user });
|
||||
|
||||
const multiLangService = new MultiLangService();
|
||||
|
||||
// 비관리자: 해당 키가 자기 회사 키인지 검증
|
||||
if (userCompanyCode !== "*") {
|
||||
const keyOwner = await multiLangService.getKeyCompanyCode(parseInt(keyId));
|
||||
if (!keyOwner) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: "다국어 키를 찾을 수 없습니다.",
|
||||
error: { code: "KEY_NOT_FOUND" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (keyOwner !== userCompanyCode) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "다른 회사의 다국어 키를 변경할 권한이 없습니다.",
|
||||
error: { code: "PERMISSION_DENIED" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await multiLangService.toggleLangKey(parseInt(keyId));
|
||||
|
||||
const response: ApiResponse<string> = {
|
||||
@@ -433,6 +550,7 @@ export const saveLangTexts = async (
|
||||
try {
|
||||
const { keyId } = req.params;
|
||||
const textData: SaveLangTextsRequest = req.body;
|
||||
const userCompanyCode = req.user?.companyCode;
|
||||
|
||||
logger.info("다국어 텍스트 저장 요청", { keyId, textData, user: req.user });
|
||||
|
||||
@@ -454,6 +572,28 @@ export const saveLangTexts = async (
|
||||
}
|
||||
|
||||
const multiLangService = new MultiLangService();
|
||||
|
||||
// 비관리자: 해당 키가 자기 회사 또는 공통(*) 키인지 검증
|
||||
if (userCompanyCode !== "*") {
|
||||
const keyOwner = await multiLangService.getKeyCompanyCode(parseInt(keyId));
|
||||
if (!keyOwner) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: "다국어 키를 찾을 수 없습니다.",
|
||||
error: { code: "KEY_NOT_FOUND" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (keyOwner !== userCompanyCode) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "다른 회사의 다국어 텍스트를 수정할 권한이 없습니다.",
|
||||
error: { code: "PERMISSION_DENIED" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await multiLangService.saveLangTexts(parseInt(keyId), {
|
||||
texts: textData.texts.map((text) => ({
|
||||
...text,
|
||||
|
||||
@@ -673,6 +673,22 @@ export class MultiLangService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 키의 소유 회사 코드 조회 (권한 검증용)
|
||||
*/
|
||||
async getKeyCompanyCode(keyId: number): Promise<string | null> {
|
||||
try {
|
||||
const result = await queryOne<{ company_code: string }>(
|
||||
`SELECT company_code FROM multi_lang_key_master WHERE key_id = $1`,
|
||||
[keyId]
|
||||
);
|
||||
return result?.company_code || null;
|
||||
} catch (error) {
|
||||
logger.error("키 소유 회사 코드 조회 실패:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 다국어 키 목록 조회
|
||||
*/
|
||||
@@ -688,6 +704,10 @@ export class MultiLangService {
|
||||
if (params.companyCode) {
|
||||
whereConditions.push(`company_code = $${paramIndex++}`);
|
||||
values.push(params.companyCode);
|
||||
} else if (params.userCompanyCode && params.userCompanyCode !== "*") {
|
||||
// 비관리자: companyCode 필터가 없으면 자기 회사 + 공통(*) 키만 반환
|
||||
whereConditions.push(`company_code IN ($${paramIndex++}, '*')`);
|
||||
values.push(params.userCompanyCode);
|
||||
}
|
||||
|
||||
// 메뉴 코드 필터
|
||||
|
||||
@@ -140,6 +140,7 @@ export interface GetLangKeysParams {
|
||||
includeOverrides?: boolean;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
userCompanyCode?: string; // 요청 사용자의 회사 코드 (비관리자 필터링용)
|
||||
}
|
||||
|
||||
export interface GetUserTextParams {
|
||||
|
||||
Reference in New Issue
Block a user