fix: 피드백 리스트 표시
Deploy EG-BIM QA Gateway / deploy (push) Successful in 35s

This commit is contained in:
root committed 2026-09-28 19:01:30 +09:00
1 parent 176316378b
commit 72c69af2fd
6 files changed
+224 -51

No files matched your search

+65 -4
View File
@@ -10,7 +10,10 @@ export default {
if (url.pathname === '/auth/callback') return finishLogin(request, env);
if (url.pathname === '/auth/logout') return logout(request, env);
if (url.pathname === '/auth/session') return sessionResponse(request, env);
if (url.pathname === '/api/feedbacks') return createFeedback(request, env);
if (url.pathname === '/api/feedbacks') {
if (request.method === 'GET') return listFeedbacks(request, env, url);
return createFeedback(request, env);
}
if (/^\/api\/feedbacks\/[^/]+\/comments$/.test(url.pathname)) return feedbackComments(request, env, url);
if (/^\/api\/feedbacks\/[^/]+$/.test(url.pathname)) return feedbackDetail(request, env, url);
@@ -187,6 +190,18 @@ function requiredAuthConfig(env) {
return keys.filter((key) => !env[key]);
}
function companyNameFromEmail(email) {
const domain = String(email || '').split('@').pop().trim().toLowerCase();
return ({
'samaneng.com': '삼안',
'hanmaceng.co.kr': '한맥기술',
'brsw.kr': '바론그룹',
'jangheon.co.kr': '장헌산업',
'jangheon.com': '(주)장헌',
'hallasanup.com': '한라산업개발'
})[domain] || '';
}
function normalizeClaims(claims) {
const profile = claims.profile && typeof claims.profile === 'object' ? claims.profile : {};
const custom = claims.customAttributes || claims.custom_attributes || {};
@@ -195,11 +210,17 @@ function normalizeClaims(claims) {
const profilePhone = profile.phone || profile.phone_number || profile.phoneNumber || profile.mobile || profile.mobilePhone || firstProfilePhone?.phoneNumber || firstProfilePhone?.phone_number || firstProfilePhone?.number || firstProfilePhone?.value || firstProfilePhone?.phone || firstProfilePhone || '';
const customPhone = custom.phone || custom.phone_number || custom.phoneNumber || custom.mobile || custom.mobilePhone || '';
const tenants = claims.tenants || claims.tenantIds || custom.tenants || [];
const tenantList = Array.isArray(tenants) ? tenants : Object.keys(tenants).map((key) => ({ id: key, ...(tenants[key] || {}) }));
const tenantList = Array.isArray(tenants) ? tenants : Object.keys(tenants).map((key) => {
const value = tenants[key];
return value && typeof value === 'object' ? { id: key, ...value } : { id: key, name: value || '' };
});
const tenantId = claims.tenant_id || claims.tenantId || custom.tenant_id || custom.tenantId || tenantList[0]?.id || tenantList[0]?.tenantId || '';
const tenantIds = tenantList.map((tenant) => typeof tenant === 'string' ? tenant : (tenant.id || tenant.tenantId || tenant.key || '')).filter(Boolean);
if (tenantId && !tenantIds.includes(tenantId)) tenantIds.unshift(tenantId);
const primaryTenant = tenantList.find((tenant) => typeof tenant !== 'string' && (tenant.id || tenant.tenantId) === tenantId) || tenantList[0] || {};
const tenantName = claims.tenant_name || claims.tenantName || custom.tenant_name || custom.tenantName || (typeof primaryTenant === 'object' ? (primaryTenant.name || primaryTenant.tenantName || primaryTenant.displayName || primaryTenant.label || '') : '');
const email = claims.email || profile.email || '';
const company = companyNameFromEmail(email) || custom.company || claims.company || claims.organization || profile.company || '';
const subject = claims.sub || claims.subject || claims.userId || claims.user_id || '';
return {
userUuid: claims.userId || claims.user_id || subject,
@@ -211,10 +232,11 @@ function normalizeClaims(claims) {
scope: claims.scope || '',
roles: claims.roles || claims.roleNames || [],
loginId: claims.email || claims.loginId || claims.preferred_username || profile.email || '',
email: claims.email || profile.email || '',
email,
name: claims.name || profile.name || claims.display_name || '',
phone: claims.phone || claims.phone_number || claims.phoneNumber || claims.mobile || claims.mobilePhone || customPhone || profilePhone,
company: custom.company || claims.company || claims.organization || profile.company || '',
tenantName,
company,
familyCompany: custom.familyCompany || custom.family_company || claims.familyCompany || claims.affiliation || '',
department: custom.team || custom.department || claims.department || primaryTenant.name || '',
position: custom.position || claims.position || profile.position || primaryTenant.position || primaryTenant.title || '',
@@ -414,6 +436,45 @@ async function createFeedback(request, env) {
return json({ id });
}
async function listFeedbacks(request, env, url) {
if (request.method !== "GET") return json({ error: "method_not_allowed" }, 405, { allow: "GET" });
const user = await getSession(request, env);
if (!user) return json({ error: "unauthenticated" }, 401);
if (!env.ABC_API_KEY) return json({ error: "feedback_api_not_configured" }, 503);
if (!env.ABC_PROJECT_ID || !env.ABC_CHANNEL_ID) return json({ error: "feedback_target_not_configured" }, 503);
const apiBaseUrl = String(env.ABC_API_BASE_URL || "https://feedback.hmac.kr").replace(/\/$/, "");
const endpoint = new URL(apiBaseUrl + "/api/projects/" + encodeURIComponent(env.ABC_PROJECT_ID) + "/channels/" + encodeURIComponent(env.ABC_CHANNEL_ID) + "/feedbacks");
["cursor", "searchText", "fieldKey", "issueName"].forEach((key) => {
const value = url.searchParams.get(key);
if (value) endpoint.searchParams.set(key, value);
});
const requestedLimit = Number(url.searchParams.get("limit") || 100);
const requestedPage = Number(url.searchParams.get("page") || 1);
endpoint.searchParams.set("limit", String(Number.isFinite(requestedLimit) ? Math.min(Math.max(Math.floor(requestedLimit), 1), 100) : 100));
endpoint.searchParams.set("page", String(Number.isFinite(requestedPage) ? Math.max(Math.floor(requestedPage), 1) : 1));
let response;
try {
response = await fetch(endpoint, { method: "GET", headers: { accept: "application/json", "x-api-key": env.ABC_API_KEY } });
} catch (error) {
console.error("feedback_list_api_request_failed", error instanceof Error ? error.message : "unknown");
return json({ error: "feedback_api_unreachable" }, 502);
}
const result = await response.json().catch(() => ({}));
if (!response.ok) {
console.error("feedback_list_api_rejected", response.status);
const upstreamMessage = String(result.message || result.error || result.code || "").slice(0, 240);
return json({ error: "feedback_api_rejected", message: upstreamMessage || "문의 목록 조회에 실패했습니다.", status: response.status }, response.status >= 500 ? 502 : response.status);
}
const items = Array.isArray(result) ? result : (result.items || result.data?.items || []);
const meta = result.meta || result.data?.meta || {};
return json({ items: Array.isArray(items) ? items : [], meta });
}
async function feedbackComments(request, env, url) {
if (request.method !== 'GET' && request.method !== 'POST') return json({ error: 'method_not_allowed' }, 405, { allow: 'GET, POST' });
const user = await getSession(request, env);