diff --git a/CHANGELOG.md b/CHANGELOG.md index a23bd7f..d41ef3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,19 @@ ADR-007 and ADR-002. ### Added +- Vendor-model photo management. New admin-gated core endpoints + `POST /api/models//image` (multipart `file`, png/jpg/jpeg/gif/webp/svg, + one image per model, replace semantics) and + `DELETE /api/models//image`, plus the public + `GET /api/models/image/` serve route. Uploads land in + `instance/modelimages/` (survives upgrades, backed up with the rest of + `instance/`) and set `models.imageurl` to the served URL; the manual Image URL + field still accepts external URLs and the shipped `/images/models/*` assets + (upload is additive). Delete only removes files we own under the instance dir. + The Models settings page grows a thumbnail, Upload/Replace, and Remove + controls in the edit modal. Asset hero images (e.g. the machine badge) read + `imageurl` unchanged, so uploaded photos render with no consumer changes. + - Application support teams with contacts, replacing the legacy supportteams/appowners pair. New core `supportteamcontacts` table (multiple named contacts per team, ordered by `sortorder`); `supportteams` keeps @@ -51,6 +64,21 @@ ADR-007 and ADR-002. no-target list with dispositions, a worked idempotent Python importer, and row-count parity checks. +### Fixed + +- Asset relationships card no longer lists a symmetric peer twice. Relationship + types gain `relationshiptypes.isdirectional` (migration + `7d19_relationshiptype_directional`; seeded false for the connection-like + types Dualpath, connectedto, Cluster Member, Serial Cable, Direct Ethernet, + USB, WiFi, true for controls/Controlled By/Backup For/Master-Slave/partof/ + defaultprinter). The card now collapses every stored direction row of a + symmetric type into one direction-blind "Connected" entry per peer (deleting + it removes all collapsed rows), while directional types drop the + Outgoing/Incoming headers for inline `Type -> peer` / `<- Type from peer` + phrasing. The type CRUD and the per-asset relationships endpoint carry + `isdirectional`; the Relationship Types settings page gains a Directional + toggle. + ## [0.6.0] - 2026-07-11 ### Added diff --git a/docs/BACKUP-RESTORE.md b/docs/BACKUP-RESTORE.md index 75d9cd3..5106ab5 100644 --- a/docs/BACKUP-RESTORE.md +++ b/docs/BACKUP-RESTORE.md @@ -18,6 +18,8 @@ app pointing at floor plans and logos that no longer exist. |------|----------|-----| | Database | MySQL `shopdb_flask` | All application data. | | `instance/branding/` | repo `instance/` dir | Uploaded logos and favicon. | +| `instance/modelimages/` | repo `instance/` dir | Uploaded vendor-model photos. | +| `instance/employeephotos/` | repo `instance/` dir | Uploaded self-hosted employee photos (external mode serves photos from the HR database instead). | | `instance/` floor plans | repo `instance/` dir | Uploaded map blueprints. | | `instance/plugins.json` | repo `instance/` dir | Which plugins this site enabled. | | `.env` | repo root (offline, secured) | Secrets needed to bring the stack back up. Store separately from the data backup, in a secrets manager. | diff --git a/docs/IMPORT-API.md b/docs/IMPORT-API.md index 9d7b300..33c9261 100644 --- a/docs/IMPORT-API.md +++ b/docs/IMPORT-API.md @@ -194,10 +194,16 @@ Per-plugin extension fields: | `vendors` | `POST /api/vendors` | `vendor` -> `vendor` | `vendor` | | `machinetypes` | `POST /api/modeltypes` | `machinetype` -> `modeltype`; set `category` (Equipment/Computer/...) | `modeltype` | | `models` | `POST /api/models` | `modelnumber`, `vendorid` (remapped), `machinetypeid` -> `modeltypeid`, `notes`, `image` -> `imageurl`, `documentationpath` -> `documentationurl` | `modelnumber` + `vendor` | + +`imageurl` imports as a plain URL string (an external URL or a legacy +`/images/models/*` path). Binary photos are not part of the import payload; +upload them after import via `POST /api/models//image` (multipart +`file`), which stores the file under `instance/modelimages/` and rewrites +`imageurl` to the served URL. | `businessunits` | `POST /api/businessunits` | `businessunit` -> `businessunit` | `businessunit` | | `operatingsystems` | `POST /api/operatingsystems` | `operatingsystem` -> `osname` | `osname` (+`osversion`) | | `machinestatus` | `POST /api/assets/statuses` | `machinestatus` -> `status` | `status` | -| `relationshiptypes` | `POST /api/assets/relationshiptypes` | `relationshiptype` -> `relationshiptype`, `description` | `relationshiptype` | +| `relationshiptypes` | `POST /api/assets/relationshiptypes` | `relationshiptype` -> `relationshiptype`, `description`, `isdirectional` (bool, default true; false = symmetric connection) | `relationshiptype` | | `notificationtypes` | `POST /api/notifications/types` | `typename`, `typedescription`, `typecolor` | `typename` | | `pctype` | `POST /api/computers/types` | `typename` -> `computertype`, `description` | `computertype` | | `subnettypes` | (see subnets) | used as `subnettype` string on subnets | - | @@ -286,6 +292,18 @@ USB devices and their history: The `checkouttime`/`checkintime` overrides are honored only in import mode. +Employee directory (people): only self-hosted mode (`employee_directory_mode = +selfhosted`) owns people in this app; import them via the directory bulk-upsert +`POST /api/employees/directory/import` (CSV headers `SSO,First_Name,Last_Name, +Team,Role,Picture`) or per-person `POST /api/employees/directory`. Photos: + +- External mode: the photo is a URL/relative path supplied by the HR database + (`Picture` column); it is a read-only pass-through and cannot be uploaded here. +- Self-hosted mode: the `Picture` CSV field is a legacy text label and does not + drive the displayed photo. Upload the real photo after import via + `POST /api/employees//photo` (multipart `file`, png/jpg/jpeg/gif/webp), + which stores it under `instance/employeephotos/` and serves it publicly. + ### 3.7 Anything unmappable -> custom fields For a legacy column with no target field (for example `machines.logicmonitorurl`, diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index 2781efc..3abf387 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -389,6 +389,15 @@ export const modelsApi = { }, delete(id) { return api.delete(`/models/${id}`) + }, + uploadImage(id, file) { + // multipart photo upload; backend sets imageurl to the served URL + const form = new FormData() + form.append('file', file) + return api.post(`/models/${id}/image`, form, { headers: { 'Content-Type': 'multipart/form-data' } }) + }, + removeImage(id) { + return api.delete(`/models/${id}/image`) } } @@ -730,6 +739,15 @@ export const employeesApi = { }, importCsv(csv) { return api.post('/employees/directory/import', { csv }) + }, + // multipart photo upload; backend sets photofilename + returns photourl + uploadPhoto(sso, file) { + const form = new FormData() + form.append('file', file) + return api.post(`/employees/${sso}/photo`, form, { headers: { 'Content-Type': 'multipart/form-data' } }) + }, + removePhoto(sso) { + return api.delete(`/employees/${sso}/photo`) } } } diff --git a/frontend/src/views/AppLayout.vue b/frontend/src/views/AppLayout.vue index 313d28c..7d129da 100644 --- a/frontend/src/views/AppLayout.vue +++ b/frontend/src/views/AppLayout.vue @@ -75,7 +75,10 @@ - + + @@ -83,7 +86,7 @@