feat(backend): slice 8 — export scene to mxdrw
- internal/exporter: pure BuildScene + 21-char base62 IDs, port ellipses, device rect+text pairs, IO diamonds, arrow bindings, legend texts. Bundles intentionally omitted per design §4.1. - internal/db: PersistExcalidrawIDs idempotent updater per project. - internal/server: POST /api/projects/:pid/sync/export — loads snapshot, mints/reuses excalidraw_ids, PUTs scene to mxdrw with bearer auth. Returns viewer URL + element_count + mxdrw response. Roundtrip hand-tested against mxdrw.msbls.de: scene saved, IDs stable across re-exports.
This commit is contained in:
60
internal/db/excalidraw_ids.go
Normal file
60
internal/db/excalidraw_ids.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
// PersistExcalidrawIDs writes the assignments returned by the exporter
|
||||
// back onto the corresponding rows. Idempotent: only updates rows whose
|
||||
// excalidraw_id is currently NULL (the first export "owns" the id; later
|
||||
// exports reuse it so mxdrw's collab cursors / undo history survive).
|
||||
//
|
||||
// Caller passes one map per kind; keys are the in-project row ids,
|
||||
// values are the 21-char Excalidraw element ids the exporter minted.
|
||||
func (s *Store) PersistExcalidrawIDs(projectID int64,
|
||||
frames, devices, ports, ios, cables map[int64]string,
|
||||
) error {
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
if err := updateExIDs(tx, "frames", projectID, frames); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updateExIDs(tx, "devices", projectID, devices); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updateExIDs(tx, "ports", projectID, ports); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updateExIDs(tx, "io_markers", projectID, ios); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updateExIDs(tx, "cables", projectID, cables); err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func updateExIDs(tx *sql.Tx, table string, projectID int64, m map[int64]string) error {
|
||||
if len(m) == 0 {
|
||||
return nil
|
||||
}
|
||||
stmt, err := tx.Prepare(
|
||||
`UPDATE ` + table + `
|
||||
SET excalidraw_id = ?
|
||||
WHERE id = ? AND project_id = ? AND excalidraw_id IS NULL`,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stmt.Close()
|
||||
for id, exID := range m {
|
||||
if _, err := stmt.Exec(exID, id, projectID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user