metis-desk/scripts/workstation-dashboard-fixtures.py

119 lines
13 KiB
Python

#!/usr/bin/env python3
"""Prepara escenarios de puestos solo en la instalación desechable de visual-audit.py."""
import json
import os
from pathlib import Path
import runpy
import tomllib
from urllib.parse import urlparse
ROOT = Path(__file__).resolve().parent.parent
AUDIT = runpy.run_path(str(ROOT / "scripts/visual-audit.py"))
def main():
state = (ROOT / os.environ["METIS_PLAYWRIGHT_STORAGE_STATE"]).resolve()
if not state.is_relative_to(ROOT / ".artifacts/playwright") or state.stat().st_mode & 0o077:
raise RuntimeError("Requiere la sesión privada del preparador sintético.")
report = json.loads((state.parent / "report.json").read_text())
if urlparse(report["origin"]).hostname != "localhost" or report.get("ordinaryUser") is not True:
raise RuntimeError("Requiere instalación local sintética con usuario ordinario.")
config = tomllib.loads((ROOT / ".codex/config.toml").read_text())["mcp_servers"]["playwright"]
code = r'''async (page) => {
const origin=__ORIGIN__, companyId=__COMPANY__;
const session=await page.request.get(origin+'/api/auth/session');
if(session.status()!==200)throw Error('Sesión no disponible');
const {csrfToken}=await session.json();
async function get(path){const r=await page.request.get(origin+'/api'+path);if(r.status()!==200)throw Error('GET '+path+': '+r.status());return r.json()}
async function post(path,data){const r=await page.request.post(origin+'/api'+path,{data,headers:{origin,'x-csrf-token':csrfToken}});if(![200,201].includes(r.status())){const j=await r.json();throw Error('POST '+path+': '+r.status()+' '+j.code)}return r.json()}
const base='/companies/'+companyId;
const details=await get(base);let company=details.company??details;
if(company.name!=='Auditoría visual — Datos sintéticos')throw Error('Compañía no sintética');
const catalog=(await get('/asset-catalog/'+companyId)).items;
const resourceCatalog=(await get('/resource-catalog/'+companyId)).items;
const inventory=await get('/assets/'+companyId);
const resources=(await get('/resources/'+companyId)).resources;
const profileRecords=(await get('/work-profiles/'+companyId)).profiles;
function template(name){const t=catalog.find(x=>x.values.kind==='template'&&x.values.name===name);if(!t)throw Error('Plantilla ausente: '+name);return t.id}
async function refreshCompany(){const d=await get(base);company=d.company??d;return company}
async function person(name){let p=company.people.find(x=>x.name===name);if(!p){await post(base+'/people',{name,position:null,email:null,phone:null,active:true,isContact:false});await refreshCompany();p=company.people.find(x=>x.name===name)}return p}
const angela=await person('Ángela Muñoz de la Peña'), inigo=await person('Íñigo Fernández del Río');
const siteName='Sede de pruebas — área administrativa';
if(!company.sites.some(x=>x.name===siteName)){await post(base+'/sites',{name:siteName,active:true,address:null,phone:null});await refreshCompany()}
const site=company.sites.find(x=>x.name===siteName);
const deptName='Administración y gestión documental';
if(!company.departments.some(x=>x.name===deptName)){await post(base+'/departments',{name:deptName,active:true});await refreshCompany()}
const department=company.departments.find(x=>x.name===deptName);
if(!company.assignments.some(x=>x.personId===angela.id&&x.departmentId===department.id)){await post(base+'/people/'+angela.id+'/assignments',{departmentId:department.id,position:'Responsable de documentación',startDate:'2026-01-01',endDate:null,isPrimary:true});await refreshCompany()}
async function asset(name,kind='Portátil'){let a=inventory.assets.find(x=>x.values.name===name);if(!a){a=(await post('/assets/'+companyId,{values:{name,templateId:template(kind),siteId:site.id,status:'operational',notes:'Fixture sintética del dashboard.',characteristics:{}}})).asset;inventory.assets.push(a)}return a}
const host=await asset('PORT-ADM-01 · Documentación y gestión');
const monitor=await asset('MON-ADM-01 · Monitor panorámico','Monitor');
const printer=await asset('IMP-RED-01 · Impresora de planta compartida','Impresora');
const lab=await asset('PC-LAB-01 · Equipo del laboratorio');
async function resource(key,name,characteristics={}){let r=resources.find(x=>x.values.name===name);if(!r){const t=resourceCatalog.find(x=>x.builtinKey==='template:'+key);if(!t)throw Error('Plantilla recurso ausente: '+key);r=(await post('/resources/'+companyId,{values:{name,templateId:t.id,active:true,notes:'Fixture sintética del dashboard.',characteristics}})).resource;resources.push(r)}return r}
function field(key){const f=resourceCatalog.find(x=>x.builtinKey==='field:'+key);if(!f)throw Error('Campo ausente: '+key);return f.id}
const app=await resource('application','Gestor documental de ejemplo');
const os=await resource('os','Sistema operativo de ejemplo');
const license=await resource('software_license','Licencia documental de ejemplo');
const certificate=await resource('certificate','Certificado de pruebas de documentación',{[field('expires')]:'2027-12-31'});
const mailbox=await resource('mailbox','Buzón de Ángela',{[field('email')]:'angela@example.invalid',[field('context')]:'Correo de pruebas'});
const unc=String.raw`\\srv-doc.example.invalid\documentacion\administracion`;
const share=await resource('shared_folder','Documentación administrativa compartida',{[field('unc')]:unc});
const tool=await resource('support','Herramienta de soporte de ejemplo');
function same(actual,expected){return Object.entries(expected).every(([key,value])=>Array.isArray(value)?Array.isArray(actual[key])&&value.length===actual[key].length&&value.every(x=>actual[key].includes(x)):key==='driveLetter'?(actual[key]??'').replace(/:$/,'')===(value??'').replace(/:$/,''):actual[key]===value)}
async function access(values){const before=(await get('/access/'+companyId)).items;const existing=before.find(x=>x.values.kind===values.kind&&(values.name?x.values.name===values.name:values.login?x.values.login===values.login:same(x.values,values)));if(existing)return existing;const saved=await post('/access/'+companyId,{values});return saved.items.find(x=>!before.some(y=>y.id===x.id))}
const system=await access({kind:'system',name:'Identidad corporativa de pruebas',active:true});
const account=await access({kind:'account',systemId:system.id,environmentId:null,login:'angela.pruebas',usage:'personal',active:true,expiresOn:null,notes:null});
const period={startDate:'2026-01-01',endDate:null};
const accountPerson=await access({kind:'account_person',accountId:account.id,personId:angela.id,...period,configuration:'configured'});
const type=share.values.integration.resourceTypeId;
const permission=await access({kind:'permission',name:'Lectura documental de pruebas',active:true,resourceTypeId:type,description:null});
const need=await access({kind:'need',personId:angela.id,resourceId:share.id,permissionIds:[permission.id],...period,notes:null});
const grant=await access({kind:'grant',resourceId:share.id,permissionId:permission.id,accountId:account.id,groupId:null,...period,configuration:'configured'});
await access({kind:'coverage',needId:need.id,grantId:grant.id,accountPersonId:accountPerson.id,membershipId:null,...period});
async function station(name,computer,personId,ids){let s=inventory.workstations.find(x=>x.values.name===name);if(!s){s=(await post('/assets/'+companyId+'/workstations',{values:{name,active:true,siteId:site.id,personId,computerAssetId:computer?.id??null,assetIds:ids}})).workstation;inventory.workstations.push(s)}return s}
let main=inventory.workstations.find(x=>x.values.name==='Administración — documentación y atención interna');
if(!main){const first=await station('Administración — puesto inicial',host,angela.id,[host.id]);main=(await post('/assets/'+companyId+'/workstations/'+first.id+'/update',{values:{...first.values,name:'Administración — documentación y atención interna',assetIds:[host.id,monitor.id]}})).workstation}
const withoutResponsible=await station('Laboratorio — equipo compartido sin responsable',lab,null,[lab.id]);
const empty=await station('Puesto pendiente de equipamiento',null,null,[]);
const retired=await station('Recepción — puesto desmantelado',null,angela.id,[]);
if(retired.values.active)await post('/assets/'+companyId+'/workstations/'+retired.id+'/decommission',{});
async function profile(name,personId,assetId,active=true,accountId=null){let p=profileRecords.find(x=>x.values.name===name);if(!p){p=(await post('/work-profiles/'+companyId,{values:{name,personId,assetId,active,accountId,notes:null}})).profile;profileRecords.push(p)}return p}
const primary=await profile('Ángela · administración',angela.id,host.id,true,account.id);
const secondary=await profile('Íñigo · consultas de documentación',inigo.id,host.id);
await profile('Ángela · perfil archivado del mismo ordenador',angela.id,host.id,false);
await profile('Ángela · laboratorio',angela.id,lab.id);
async function configuration(path,values,collection='configurations'){const items=(await get('/'+path+'/'+companyId))[collection];if(!items.some(x=>same(x.values,values)))await post('/'+path+'/'+companyId,{values})}
await configuration('software-installations',{assetId:host.id,resourceId:app.id,licenseResourceId:license.id,version:'4.2',state:'configured',notes:null});
await configuration('software-installations',{assetId:host.id,resourceId:os.id,licenseResourceId:null,version:null,state:'configured',notes:null});
const mailboxUses=(await get('/mailboxes/'+companyId)).uses;
if(!mailboxUses.some(x=>x.values.personId===angela.id&&x.values.mailboxId===mailbox.id))await post('/mailboxes/'+companyId+'/uses',{values:{mailboxId:mailbox.id,personId:angela.id,...period}});
await configuration('folder-mappings',{profileId:primary.id,needId:need.id,accessAccountId:account.id,path:unc,driveLetter:'Z',state:'configured',notes:null},'mappings');
await configuration('mail-configurations',{profileId:primary.id,mailboxId:mailbox.id,method:'imap',state:'configured',notes:null});
await configuration('application-configurations',{profileId:primary.id,resourceId:app.id,delivery:'installed',version:'4.2',state:'configured',notes:null});
await configuration('printer-configurations',{profileId:primary.id,printerId:printer.id,connection:'network',isDefault:true,state:'configured',notes:null});
await configuration('certificate-configurations',{profileId:primary.id,resourceId:certificate.id,location:null,state:'pending',notes:'Instalación pendiente de completar.'});
await configuration('telework-authorizations',{personId:angela.id,active:true,startsOn:'2026-01-01',expiresOn:'2027-08-31',notes:null},'authorizations');
await configuration('remote-connections',{personId:angela.id,sourceKind:'company_asset',sourceAssetId:host.id,targetKind:'network',targetId:null,siteId:site.id,method:'VPN corporativa documentada',requiresVpn:true,state:'configured',notes:null},'connections');
const support=await get('/asset-support/'+companyId+'/'+host.id);
if(!support.connections.length)await post('/asset-support/'+companyId+'/'+host.id,{values:{resourceId:tool.id,destination:'equipo-adm-01.example.invalid',requiresVpn:false,preferred:true,state:'available',notes:''}});
const denseAssets=[];
for(let i=0;i<80;i++){denseAssets.push(await asset('DASH-DENSE-'+String(i).padStart(2,'0')+(i%10===0?' — Equipo de documentación e ingeniería con nombre extenso':'')))}
const dense=await station('Puesto de pruebas — alta densidad',denseAssets[0],null,denseAssets.map(x=>x.id));
let extra=0;
while(company.people.filter(x=>x.active).length<52){await person('DASH-PERSON-ACTIVE-'+String(extra++).padStart(2,'0'))}
const activePeople=company.people.filter(x=>x.active).slice(0,50);
for(let i=0;i<activePeople.length;i++){await profile('DASH-PROFILE-'+String(i).padStart(2,'0')+' · '+activePeople[i].name,activePeople[i].id,denseAssets[0].id)}
for(let i=0;i<10;i++){await profile('DASH-ARCHIVED-'+i,activePeople[i].id,denseAssets[0].id,false)}
const other=(await get('/companies')).companies.find(x=>x.id!==companyId);
return {companyId,origin,main:main.id,withoutResponsible:withoutResponsible.id,empty:empty.id,retired:retired.id,dense:dense.id,primaryProfile:primary.id,secondaryProfile:secondary.id,host:host.id,lab:lab.id,otherCompany:other.id,counts:{companies:(await get('/companies')).companies.length,people:company.people.length,assets:(await get('/assets/'+companyId)).assets.length,resources:(await get('/resources/'+companyId)).resources.length,denseMembers:dense.values.assetIds.length,denseProfiles:60}};
}'''.replace("__ORIGIN__", json.dumps(report["origin"])).replace("__COMPANY__", json.dumps(report["companyId"]))
with AUDIT["browser"](config, state) as client:
result = AUDIT["result_json"](AUDIT["tool"](client, "browser_run_code_unsafe", {"code": code}))
(state.parent / "workstation-fixtures.json").write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n")
print(json.dumps(result, ensure_ascii=False))
if __name__ == "__main__":
main()