This commit is contained in:
1 parent
176316378b
commit
72c69af2fd
6 files changed
+224
-51
No files matched your search
+65
-4
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user