import getTokenHelper from "../../../utils/getTokenHelper" import refreshTokenHelper from "../../../utils/refreshTokenHelper" import fetchHelper from "../../../utils/fetchHelper" /** * Resolves whether the logged-in user is allowed to manage product images, * driven by the IsEditProductImage flag on the org's linked C_BPartner. * Returns { canEditProductImage, orgId, bpartnerId }. * * Why this endpoint exists (and why it does NOT reuse the direct * /organizations/{id}/bpartner read): * - A limited customer role ('c') is scoped to its OWN org (e.g. wonderhome * 1000008) and is denied/filtered on the admin tables ad_user / ad_org / * c_bpartner via REST, so reading the flag with the USER's token returns * nothing. We therefore read with the iDempiere service token (system, * role-independent) — the same pattern adUserCredentials.ts uses for ad_user. * - The org cookie is not a reliable source for limited users, so resolve the * org straight from the user record (ad_user.AD_Org_ID), falling back to the * org cookie when the user record has no usable org. * * Two requests (ad_user itself does not support expanding AD_Org_ID): * 1. models/ad_user/{userId}?$select=AD_Org_ID -> the user's org id * 2. models/ad_org/{orgId}?$expand=C_BPartner_ID -> linked partner */ const truthy = (v: any) => v === true || v === 'Y' || v === 'y' || v === 'true' || v === 'Yes' || v === 1 || v === '1' // iDempiere OData response casing varies by model, so match the flag key // case-insensitively and accept every truthy representation it may return. const readEditImageFlag = (bpartner: any): boolean => { if (!bpartner || typeof bpartner !== 'object') return false for (const k of Object.keys(bpartner)) { if (k.toLowerCase() === 'iseditproductimage') return truthy(bpartner[k]) } return false } const handleFunc = async (event: any, authToken: string | null = null) => { const config = useRuntimeConfig() // Service token sees every org/column regardless of the caller's role; the // limited 'c' token cannot read these admin tables. Fall back to the user // token only if the env var is unset. const serviceToken = (config.api as any)?.idempieretoken const token = serviceToken || authToken || await getTokenHelper(event) const userId = getCookie(event, 'logship_user_id') const cookieOrgId = getCookie(event, 'logship_organization_id') // 1. Resolve the user's own org (reliable for limited roles); fall back to cookie. let orgId: any = cookieOrgId if (userId) { const userRes: any = await fetchHelper(event, `models/ad_user/${userId}?$select=AD_Org_ID`, 'GET', token, null) const userOrgId = userRes?.AD_Org_ID?.id if (userOrgId && Number(userOrgId) > 0) { orgId = userOrgId } } if (!orgId || Number(orgId) <= 0) { return { canEditProductImage: false, orgId: orgId ? Number(orgId) : null, bpartnerId: null } } // 2. Pull the org's linked C_BPartner inline via $expand. const orgRes: any = await fetchHelper(event, `models/ad_org/${orgId}?$expand=C_BPartner_ID`, 'GET', token, null) const bpartner: any = orgRes?.C_BPartner_ID ?? null return { canEditProductImage: readEditImageFlag(bpartner), orgId: Number(orgId), bpartnerId: bpartner?.id ?? null } } export default defineEventHandler(async (event) => { let data: any = { canEditProductImage: false } try { data = await handleFunc(event) } catch (err: any) { try { const authToken: any = await refreshTokenHelper(event) data = await handleFunc(event, authToken) } catch (error: any) { // Fail-closed: never block the page, just deny the extra capability. data = { canEditProductImage: false } } } return data })