40 lines
3.0 KiB
JavaScript
40 lines
3.0 KiB
JavaScript
/** Scoped opt-in proxy. Never intercept the existing V1 schedule. */
|
|
export function createPlannerV4Bridge({configuredPrognosisPlant,roleAllowed,bodyJson,json,
|
|
deviceActivation,checkDeviceRate,ownedLicenseState,serviceToken,
|
|
upstreamUrl='http://netplan-v4:9100',fetchImpl=fetch}){
|
|
const base=new URL(upstreamUrl);
|
|
if(!['http:','https:'].includes(base.protocol)||base.username||base.password)throw new Error('Invalid V4 URL');
|
|
async function forward(req,res,installation,suffix,method){
|
|
if(!serviceToken){json(res,503,{error:'V4 ist nicht angebunden.'});return;}
|
|
const url=new URL(`/internal/v2/prognosis/${encodeURIComponent(installation)}/planner${suffix}`,base);
|
|
let response;
|
|
const payload=method==='GET'?undefined:await bodyJson(req);
|
|
try{response=await fetchImpl(url,{method,headers:{'X-Enelix-Service-Token':serviceToken,'Content-Type':'application/json'},body:payload===undefined?undefined:JSON.stringify(payload),signal:AbortSignal.timeout(10000),redirect:'error'});}
|
|
catch{json(res,503,{error:'V4 nicht erreichbar. Bestehende Regelung bleibt unveraendert.'});return;}
|
|
const data=await response.json().catch(()=>null);
|
|
if(!response.ok){const code=[400,401,403,404,409,413].includes(response.status)?response.status:503;json(res,code,{error:code===409?'Versionskonflikt: Ansicht neu laden.':'V4-Anfrage abgelehnt oder Dienst nicht verfuegbar.'});return;}
|
|
if(!data||typeof data!=='object'){json(res,503,{error:'Ungueltige V4-Antwort.'});return;}
|
|
json(res,200,data);
|
|
}
|
|
return async function(req,res,url){
|
|
const c=url.pathname.match(/^\/api\/plants\/([0-9a-f-]{36})\/prognosis\/planner-v4(?:\/(settings|replan))?$/i);
|
|
if(c){
|
|
const action=c[2]||'',method={'':'GET',settings:'PUT',replan:'POST'}[action];
|
|
if(req.method!==method){json(res,405,{error:'Methode nicht erlaubt.'});return true;}
|
|
const access=configuredPrognosisPlant(req,res,c[1],method!=='GET');if(!access)return true;
|
|
if(Number(access.license.quantities.grid_schedule||0)<1){json(res,403,{error:'Netzfahrplan nicht lizenziert.'});return true;}
|
|
if(method!=='GET'&&!roleAllowed(access.session,['owner','admin','operator'])){json(res,403,{error:'Keine Aenderungsberechtigung.'});return true;}
|
|
await forward(req,res,access.plant.installation_id,action?`/${action}`:'',method);return true;
|
|
}
|
|
const d=url.pathname.match(/^\/api\/v1\/installations\/([0-9a-f-]{36})\/prognosis\/planner-v4(?:\/(operation|ack|measurements))?$/i);
|
|
if(d){
|
|
const action=d[2]||'';if(req.method!==(action?'POST':'GET')){json(res,405,{error:'Methode nicht erlaubt.'});return true;}
|
|
if(!checkDeviceRate(req,d[1],'planner-v4')){json(res,429,{error:'Zu viele Anfragen.'});return true;}
|
|
const activation=deviceActivation(req,res,d[1]);if(!activation)return true;
|
|
if(Number(ownedLicenseState(activation.plant_id).quantities.grid_schedule||0)<1){json(res,403,{error:'Netzfahrplan nicht lizenziert.'});return true;}
|
|
await forward(req,res,d[1],action==='operation'?'/inputs/operation':action==='measurements'?'/measurements':action?'/ack':'',req.method);return true;
|
|
}
|
|
return false;
|
|
};
|
|
}
|