Seed the public catalog from domain/library.ts
- Catalog table ids (device_categories, port_types, cable_types, device_templates) switch from uuid to text so the existing stable, human-readable ids (pt-hdmi, dt-display, ...) survive the move instead of every diagram's references silently orphaning. - supabase/seed.sql is generated (scripts/generate-seed.mjs), not hand-written, so the seed data can't drift from the actual source of truth in domain/library.ts. Re-run the script after editing the built-in library. - Also committing ideas.md/organized-ideas.md, which have been driving every backend decision this whole project but were never actually checked in. RLS test suite re-run clean (23/23) after both the schema change and the seed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017DUU6CnxECCDeqDNYJgr5x
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
* Remove hard coded devices
|
||||
* Create back end with storage
|
||||
* Determine relational or nosql for storage
|
||||
* Users:
|
||||
* Types:
|
||||
* regular - has own account. can CRUD own devices. can create own diagrams. can use public devices in diagrams. can submit a new device for review or suggestion for device modification.
|
||||
* Admin - same as regular user, but additionally: can view/approve devices and suggestions
|
||||
* Super Admin - same as Admin, but additionally: can CRUD users and user devices/diagrams, etc. Has full access to CRUD all data
|
||||
|
||||
# Device Compact view
|
||||
* Devices should have a compact state that doesn't show individual connection points, but rather an aggregate for inputs/outputs/bidirectional. The device should show connected/not connected numbers for inputs/outputs/bidirectional so the user can see what's still got open ports
|
||||
* a device can be expanded to show the full individual ports
|
||||
* The compact view should show the name of the device, but not the make/model
|
||||
* There should be a virtual cable for each type of connection to another device, but all similar connections (e.g. inputs) to the same device should be shown as a single virtual cable
|
||||
* aggregated cables should have a special name or tag or something that lets the user know it's an aggegate.
|
||||
|
||||
# Device Expanded view
|
||||
* Devices should have an expanded view that shows all connection points with name and type, the device name, make and model, and have individual virtual cables for each connection point.
|
||||
|
||||
* There should be two different device menus. One is for public devices. One is for user defined devices.
|
||||
|
||||
# Public device menu
|
||||
* Should be searchable, and have multiple views: category>manufacturer and manufacturer>category
|
||||
* These device definitions cannot be directly modified by a regular user. Instances of them can be used by regular users in their own diagrams.
|
||||
* regular users can submit modified versions for review (to help make the device definitions better for all users)
|
||||
|
||||
# User device menu
|
||||
* should be searchable and viewed by device category (adapter, effect, mixer, etc)
|
||||
* These are user defined devices.
|
||||
* They are only viewable to the user that created them (and admins, but not other regular users)
|
||||
* user devices can be submitted for adding as a public device.
|
||||
|
||||
* Port types and cable types should work effectively like devices: there's a public set and then users can create their own as needed, and submit for addition to the public set.
|
||||
|
||||
* There should be an admin interface for admins and super admins
|
||||
|
||||
# Admin/super admin interface
|
||||
* should allow admins to view and approve/reject submissions for devices/ports/cables
|
||||
* should allow admins to CRUD public devices/ports/cables
|
||||
* should allow super admins to CRUD all devices/ports/cables
|
||||
* should allow super admins to view users diagrams
|
||||
* should allow super admins to CRUD users diagrams
|
||||
* should allow super admins to CRUD user accounts
|
||||
* should not allow anyone to see/edit a user's password.
|
||||
|
||||
# Bill of Materials view
|
||||
* Cables of the same type should be grouped together, with individual cable lengths of that type available to see when expanded with a chevron.
|
||||
* Cables of the same type and length should show their own count and total length
|
||||
* User should be able to input cost for each cable type and length, and for each device in the diagram
|
||||
* a cost total should be available to view
|
||||
|
||||
# Diagram menu
|
||||
* a user should be able to see a list of all their own saved diagrams, and should be able to load/delete individual ones
|
||||
|
||||
# user signup
|
||||
* users can use either google single sign in or manually sign up
|
||||
* if using google, they only need to provide a user name, we'll gather email from Google
|
||||
* if manual sign up, the user will provide a user name, password, and (valid) email
|
||||
* determine best option for sending emails
|
||||
* username must be unique
|
||||
|
||||
# user sign in
|
||||
* users can use either google sign in or manually enter username/password
|
||||
* determine possible path for allowing users to change their email
|
||||
* determine possible path for migrating a user account from one google account to another
|
||||
@@ -0,0 +1,147 @@
|
||||
# AV Planner — Robustness Plan
|
||||
|
||||
Organized from `ideas.md`, plus decisions made while working through it together on 2026-09-04. Decided items are marked **Decided**; items I'm recommending but that are still yours to confirm are marked **Recommended**; things nobody's answered yet are marked **Open Question** and are also collected in one list at the end.
|
||||
|
||||
## 0. Guiding principle: local-first, no premature lock-in
|
||||
|
||||
Stated explicitly during planning: this needs to run **entirely locally** during development, with zero cost and zero cloud dependency, while still getting BaaS-level convenience (auth, roles, storage) instead of hand-rolling all of it. The choice to go to a specific cloud vendor is deferred until the app is actually ready for production. Every recommendation below is filtered through that constraint — anything that only runs as someone else's hosted service is out.
|
||||
|
||||
- **Decided: this is a development-time constraint, not a shipped product feature.** The deployed app is a normal networked web app that talks to a real backend — no offline mode, local cache/sync layer, or conflict resolution for disconnected use is in scope. "Local-first" describes how *you* build it, not a capability end users get.
|
||||
|
||||
---
|
||||
|
||||
## 1. Backend & Data Architecture
|
||||
|
||||
- **Decided: Supabase, self-hosted.** Postgres underneath, with built-in auth (Google OAuth included), storage, and row-level security (RLS) that maps cleanly onto the regular/admin/super-admin model in section 2.
|
||||
- **Decided (resolves "determine relational or NoSQL"): relational / Postgres.** Falls directly out of the Supabase choice, and fits this data anyway — devices, ports, cables, diagrams, and users all have real relationships and referential integrity matters (e.g. a diagram shouldn't reference a device type that's been deleted).
|
||||
- **Local dev:** the Supabase CLI (`supabase start`) spins up the full stack — Postgres, Auth (GoTrue), REST API (PostgREST), Storage, Realtime, and a local Studio admin UI — in Docker, on your machine, for free. No account or network access needed to develop.
|
||||
- **No lock-in path:** because it's just Postgres + open-source services under the hood, "going to production" later is a real choice, not a foregone conclusion — options are Supabase Cloud (managed, easiest), self-hosting the same Docker stack on your own server, or exporting the Postgres database to any other Postgres host. None of the application code needs to change based on which of these you pick.
|
||||
- **Frontend integration:** the app already isolates storage behind a `ProjectRepository` interface (see `src/data/`), specifically so a real backend could be swapped in without touching UI/state code. A Supabase-backed repository implementation slots into that seam directly.
|
||||
- **Revised: diagrams reference catalog entries by id (live reference), not a full embedded snapshot.** Superseded from an earlier pass — see the discussion recorded in §3, which walked through concrete scenarios and decided live references are net better: they let your own catalog edits (e.g. a price change on a custom cable type) propagate across all your diagrams automatically, and let cosmetic fixes to public entries reach diagrams that already used them, at the cost of a public entry's *compatibility-affecting* fields being able to affect existing diagrams if edited carelessly — which §3's impact-check-before-editing and public-entries-are-never-deleted decisions specifically guard against. Placing a device still copies its *ports* (fresh ids, independent of the template afterward) — that part of the original pattern is unchanged; it's specifically port/cable/category/manufacturer *type* definitions that are referenced live, not copied.
|
||||
- **Decided: schema changes go through git-tracked migrations via the Supabase CLI**, not ad hoc edits in the local Studio UI — keeps local dev and eventual production reproducible and reviewable, same as any other code change.
|
||||
- **Decided: automated tests specifically for the RLS policies.** These are the actual security boundary once roles matter, and they're easy to get subtly wrong in a way that fails open (exposes data that shouldn't be visible) rather than fails loud — worth explicit test coverage even if the rest of the app is tested more lightly.
|
||||
|
||||
## 2. Authentication & Users
|
||||
|
||||
**Roles** (restated as a table):
|
||||
|
||||
| Role | Can do |
|
||||
|---|---|
|
||||
| Regular | CRUD own devices; create/own diagrams; use public devices in diagrams; submit new devices or edits to existing ones for review |
|
||||
| Admin | Everything Regular can, plus: view/approve/reject device & port/cable-type submissions |
|
||||
| Super Admin | Everything Admin can, plus: CRUD *all* devices/ports/cables (public or anyone's); view and CRUD *any* user's diagrams; CRUD user accounts. Cannot see/edit a user's password (Supabase Auth stores credentials itself — the app never has access to raw or hashed passwords). |
|
||||
|
||||
- **Recommended:** enforce this at the database layer with Postgres row-level security policies keyed off role, not just in application code — it's the natural fit for Supabase and means a bug in the UI can't accidentally expose another user's data.
|
||||
- **Signup/signin:** Google SSO (username collected up front, email pulled from Google) or manual signup (username, password, verified email) — both handled by Supabase Auth directly, so no custom password-hashing/session code needed. Username must be unique — enforced with a DB unique constraint.
|
||||
- **Decided: accounts auto-link on matching verified email.** If someone signs up manually with an email and later signs in with Google using that same address, treat it as one account rather than creating a second, confusing one — matches the far more common real-world case (same person) over the rarer risk case.
|
||||
- **Decided: baseline abuse prevention from the start**, not deferred — basic rate limiting on auth (leaning on what Supabase Auth provides out of the box) plus a soft cap on pending submissions per user (see §3). Cheap to build in now, expensive to retrofit once there's real usage and real abuse; self-hosting means infra abuse cost lands on you directly, not a SaaS vendor.
|
||||
- **Decided: email verification is a hard gate.** A manually-signed-up user must verify their email before creating diagrams, submitting devices, or doing anything else — no unverified account accumulates real data. Google-SSO users are implicitly verified via Google, so this only affects the manual-signup path.
|
||||
|
||||
### Determine: best option for sending emails
|
||||
**Recommended.** Auth emails (signup confirmation, password reset) go through Supabase Auth's SMTP integration — Supabase doesn't send mail itself, it relays through whatever SMTP server you configure, which keeps this swappable-by-design (change SMTP credentials, not code). For local dev, the Supabase CLI includes **Inbucket**, a fake mail catcher with a web UI, so you can test signup/reset flows locally without sending real email or paying anything. For production, plug in any standard transactional-email provider over SMTP — **Resend** is a reasonable default (generous free tier, simple setup), with Postmark or Amazon SES as alternatives if deliverability or cost at higher volume becomes a concern. This is a config change, not an architecture decision, and doesn't need to be locked in now.
|
||||
|
||||
### Determine: path for allowing users to change their email
|
||||
**Recommended.** Supabase Auth has this built in (`updateUser({ email })`): the user requests the change, a confirmation link is sent to the *new* address, and the email only actually updates once that's confirmed. No custom flow to build — just wire up the UI for it. Username stays separate from email and keeps its own uniqueness check.
|
||||
|
||||
### Determine: path for migrating a user account from one Google account to another
|
||||
**Recommended**, two-tier:
|
||||
1. **Self-service (has access to both accounts):** Supabase Auth supports linking a second OAuth identity to an existing account (`linkIdentity`). The user signs in with the old Google account, links the new one, then unlinks the old — same internal account/UUID throughout, so all owned diagrams and devices carry over with zero data migration.
|
||||
2. **Lost access to the old account:** needs a human — a Super Admin tool to reassign an account's linked identity or merge two accounts (transfer diagrams/devices from A to B). This is an edge case, not needed for MVP; flagging it now so the Super Admin "CRUD user accounts" capability is built with this in mind rather than needing rework later.
|
||||
|
||||
---
|
||||
|
||||
## 3. Device / Port / Cable Type Library (Public vs. User-Defined)
|
||||
|
||||
- Devices, port types, and cable types all follow the **same pattern**: a public, curated catalog plus each user's own private catalog.
|
||||
- Public catalog entries can't be edited directly by regular users; they can only submit a new entry or a suggested edit to an existing one, which goes into a review queue for Admins.
|
||||
- A user's own catalog entries are visible only to that user (and Admins/Super Admins), and can be submitted for promotion to the public catalog.
|
||||
- **Seeding:** the app's current hardcoded libraries (`domain/library.ts` — port types, cable types, device templates, categories) become the initial seed data for the public catalog once the backend exists, rather than being thrown away.
|
||||
- **Decided: review/approval experience.** An Admin reviewing a submission sees a **diff against the current public entry** (not just the raw submitted version), and the submitter is **notified either way** — approved or rejected, with a reason attached on rejection rather than a silent disappearance.
|
||||
- **Decided: notify in-app, not just by email.** A bell icon/activity feed surfaces submission outcomes for submitters and a pending count for Admins — email is fine as the durable record, but a faster in-app loop matters for an Admin actively working through the queue.
|
||||
- **Decided: a light duplicate-detection nudge for Admins** reviewing new device submissions — fuzzy-match the proposed name against existing public entries and flag likely duplicates, so the catalog doesn't slowly fill with near-identical entries as more people submit.
|
||||
- **Decided: a soft cap on pending submissions per user** (exact number TBD when this is built), as part of the baseline abuse prevention in §2 — stops one user from flooding the review queue.
|
||||
- **Decided: approval promotes the submitter's entry in place**, rather than creating a separate new public entry. Same id, just flips from private to public — the submitter's existing diagrams (which already snapshotted it, per §1) don't need re-pointing, and there's no leftover duplicate private copy sitting alongside the new public one.
|
||||
- **Decided: rejected submissions stay editable for resubmission**, not deleted or dead-ended. The submitter can revise based on the Admin's stated reason and resubmit the same submission rather than starting over from scratch.
|
||||
- **Decided: "manufacturer" becomes a normalized, submittable catalog** — the same public/user-defined pattern already used for device categories, rather than free text on each device. Keeps the manufacturer↔category browse view in §5 clean and gives the duplicate-detection nudge above a much more reliable signal (matching manufacturer + similar model name) than name-matching alone.
|
||||
- **Decided: catalog entries reference by id, accepting the risk that a careless edit to a public entry could affect diagrams already using it** (see the revised §1 note) — worked through via concrete scenarios: this is strictly better for the common cases (your own edits propagating across your diagrams, cosmetic fixes to public entries reaching existing users), and if an edit does surface a real problem, that's arguably diagnosing something that was actually wrong rather than "breaking" a diagram. Two things make the remaining risk manageable rather than ignored:
|
||||
- **Impact check before an Admin acts.** Before an edit-submission is approved (or a Super Admin edits/hides a public entry directly), they see how many diagrams reference that entry and a short sample (diagram name + owner username) — computed by a privileged, admin-only function that returns only that aggregate, *not* raw access into other users' diagram contents. Regular Admins still don't get diagram visibility (only Super Admins do, per §6) — this doesn't change that boundary, it just answers "what's the blast radius" without needing it.
|
||||
- **Public catalog entries are never hard-deleted**, only hidden/unpublished from new use (the same pattern already used for hiding built-in device templates) — closes off the one scenario (an entry disappearing out from under a diagram, showing "Unknown type") that live references can't otherwise defend against.
|
||||
- **Decided: site-wide announcements.** A new lightweight concept: an Admin can post a dismissible banner that every signed-in user sees (e.g. "the XLR-to-TRS adapter cable's compatibility changed on 2026-09-08, check any diagrams using it") — offered as a follow-up step right after acting on an impact-flagged change, so users aren't surprised by something that already shipped.
|
||||
|
||||
## 4. Canvas — Compact & Expanded Device Views
|
||||
|
||||
**Compact view:**
|
||||
- Shows device name only (no make/model) plus aggregate connected/open counts per input/output/bidirectional group, so you can tell what's still available at a glance without seeing every individual port.
|
||||
- Connections to another device are drawn as a single "virtual cable" per connection-type group rather than one line per physical port, when more than one real connection of that type exists between the same two devices.
|
||||
- Aggregated virtual cables need a visual marker (tag/label/style) so it's obvious at a glance that a line represents more than one real cable.
|
||||
|
||||
**Expanded view:**
|
||||
- What the app already does today — every port shown individually with name and type, full device name/make/model, one real line per real connection.
|
||||
|
||||
- **Recommended:** whatever visual grouping happens in compact view must be purely a canvas rendering concern — the BOM math (cable counts, lengths, totals) always operates on the real underlying connections, never on the aggregated display. Otherwise compact view could silently produce a wrong shopping list.
|
||||
- **Implementation detail, still loose:** exactly what "connection-type group" means for aggregation purposes isn't fully pinned down — grouped by cable type, by port family, by direction, some combination? Low-stakes to leave open since this is frontend-only and easy to iterate on visually once it's being built.
|
||||
- **Decided: toggle scope.** Per-device (each device node expands/collapses independently), defaulting to **compact** for a newly-placed device — consistent with the palette categories already defaulting to collapsed. Plus **"Expand all" / "Collapse all"** buttons on the canvas for quickly toggling every device at once, rather than clicking through each one individually.
|
||||
|
||||
## 5. Device Menus
|
||||
|
||||
- **Public device menu:** searchable; browsable both by category→manufacturer and manufacturer→category (two view modes, not a single fixed hierarchy).
|
||||
- **User device menu:** searchable, browsed by category (the app's existing category system — see the categories feature already built — extends naturally here). Visible only to its owner (and Admins/Super Admins). Entries can be submitted for public promotion from here.
|
||||
|
||||
## 6. Admin / Super Admin Interface
|
||||
|
||||
| Capability | Admin | Super Admin |
|
||||
|---|---|---|
|
||||
| View/approve/reject device & port/cable-type submissions | ✓ | ✓ |
|
||||
| CRUD public devices/ports/cables | ✓ | ✓ |
|
||||
| CRUD *any* device/port/cable (public or user-owned) | | ✓ |
|
||||
| View any user's diagrams | | ✓ |
|
||||
| CRUD any user's diagrams | | ✓ |
|
||||
| CRUD user accounts | | ✓ |
|
||||
| Suspend/ban a user account (reversible, without deleting their data) | | ✓ |
|
||||
| View/edit a user's password | never — nobody, at any role |
|
||||
|
||||
- **Decided: suspend/ban is a separate, lighter capability from delete.** Blocks login without destroying the account's data, so a problem user can be dealt with without an irreversible action.
|
||||
|
||||
## 7. Bill of Materials Enhancements
|
||||
|
||||
- Cables of the same type grouped together; expand with a chevron to see the individual cable-length entries within that type.
|
||||
- Cables that also share the same *length* get their own subtotal count and total length.
|
||||
- Cost input for each cable (by type+length) and for each device in the diagram; a grand total cost across the diagram.
|
||||
- **Recommended:** store cost as an optional *suggested default* on the device/cable type definition (public or user-defined), but let each diagram override it — public catalog prices will drift out of date and vary by vendor/region, so the diagram-level number should always win. This also means public catalog entries don't need price moderation as part of the review workflow.
|
||||
|
||||
## 8. Diagram Management
|
||||
|
||||
- A user can see, load, and delete their own saved diagrams.
|
||||
- **Decided:** diagrams can be shared with specific people (not public-gallery-by-default, not strictly private-only) — this adds a collaborators concept to the data model (diagram owner + a list of users with access).
|
||||
- **Decided: collaborator permissions.** Configurable per-collaborator — the owner picks view-only or edit access for each person they share with individually, rather than one fixed permission level for everyone the diagram is shared with.
|
||||
- **Implementation note:** this makes the diagram RLS policies meaningfully more complex than a simple "owner_id = you" check — they need to account for owner access, per-collaborator permission level, and Super Admin override all composing correctly. Worth keeping specifically in mind when the RLS test coverage from §1 gets written for diagrams.
|
||||
- **Decided: co-editing model.** Turn-based shared access, not real-time simultaneous co-editing. Collaborators (with edit access) can make changes, but there's no live-cursor/multiplayer layer — no realtime conflict-resolution engine needed for this.
|
||||
- **Decided: naming.** Rename "Project" to "Diagram" throughout the app (code, UI, docs) for consistency with the notes and this plan. This includes renaming the `ProjectRepository` interface and its implementations (see §1) as part of that pass.
|
||||
- **Future idea, not in scope now:** a "Project" could later become its own concept above Diagram — a folder-like container holding multiple related Diagrams (e.g. all the diagrams for one physical rig or one client). Worth keeping the data model open to this later without building it now.
|
||||
- **Decided: keep a rolling window of recent diagram snapshots** (e.g. last N saves, or last 30 days — exact policy TBD when this is built) as an undo/recovery safety net. Not full version control, but now that turn-based collaborators can edit a diagram, a bad edit — yours or theirs — needs to be recoverable.
|
||||
- **Decided: migration path for existing local data is the app's existing JSON export/import**, not a dedicated importer. Export the current local diagram, sign up for a real account, import it in — no new code required.
|
||||
|
||||
---
|
||||
|
||||
## 9. Suggested Phased Rollout
|
||||
|
||||
The full scope above is a lot to build at once. A rough sequence that keeps each phase shippable and testable on its own:
|
||||
|
||||
1. **Local backend foundation** — stand up self-hosted Supabase locally with git-tracked migrations from day one, migrate from localStorage-only to a real `DiagramRepository` implementation backed by it (renamed from today's `ProjectRepository`, per the naming decision in §8), single-user only (no roles/sharing/public library yet). Diagrams already snapshot catalog data rather than live-referencing it, per §1. This alone turns the app from a browser-toy into something with durable, real storage.
|
||||
2. **Auth** — Google SSO + manual signup/signin via Supabase Auth, tied to the diagrams from phase 1 (one user, own diagrams). Includes account auto-linking on matching email, the hard email-verification gate, and baseline abuse-prevention/rate-limiting from §2.
|
||||
3. **Multi-diagram management** — the diagram list/load/delete UI, since today's app only ever has one active diagram (currently called "Project" in code, pending the rename). Existing JSON export/import doubles as the migration path for current local data, per §8.
|
||||
4. **Public vs. private device/port/cable catalogs + submission workflow** — the biggest chunk of new complexity; probably worth its own sub-phasing (public *read* catalog first, then user-submission, then the Admin review queue with its diff view, in-app notifications, duplicate-detection nudge, in-place promotion on approval, resubmittable rejections, and per-user submission cap, all per §3). Includes the normalized manufacturer catalog alongside categories.
|
||||
5. **Roles & Admin/Super Admin interface** — layer RBAC in once there's something (submissions, other users' data) that actually needs gating. Includes account suspend/ban (§6). RLS policy test coverage (§1) belongs here too, alongside the policies themselves — especially for diagrams, once collaborator permissions make those policies non-trivial (§8).
|
||||
6. **Diagram sharing/collaborators** — turn-based access with per-collaborator view/edit permissions, plus the rolling-snapshot undo/recovery safety net, per §8 — the two are related (recoverability matters more once someone besides the owner can edit).
|
||||
7. **BOM cost tracking** — largely independent of the backend work; could realistically be pulled earlier if you want a quick visible win.
|
||||
8. **Compact/expanded canvas views + aggregated cables** — also largely independent/frontend-only; another candidate to pull earlier if desired.
|
||||
|
||||
This is a suggested order, not a commitment — happy to reshuffle if priorities change.
|
||||
|
||||
---
|
||||
|
||||
## 10. Status
|
||||
|
||||
Everything raised across all three planning passes has been answered and folded into the relevant sections above as **Decided** — nothing is currently blocked on a decision. Two things intentionally left loose rather than answered, both low-stakes and easy to settle when actually built: the future "Project groups multiple Diagrams" idea noted in §8, and the exact grouping rule for aggregated virtual cables in compact view (§4).
|
||||
|
||||
A fourth pass (while implementing §3) revised the §1 snapshot-vs-reference decision — catalog entries are referenced live by id, not embedded as full copies — and added the impact-check-before-editing, never-hard-delete-public-entries, and site-announcements decisions to §3 as the mitigations that make that safe in practice.
|
||||
@@ -0,0 +1,62 @@
|
||||
// Generates supabase/seed.sql from src/domain/library.ts, so the public
|
||||
// catalog seed data is derived directly from the source of truth instead of
|
||||
// hand-transcribed (and risking drift/typos). Run via: node scripts/generate-seed.mjs
|
||||
import { BUILT_IN_CABLE_TYPES, BUILT_IN_DEVICE_CATEGORIES, BUILT_IN_DEVICE_TEMPLATES, BUILT_IN_PORT_TYPES } from '../src/domain/library.ts'
|
||||
import { writeFileSync } from 'node:fs'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
function sqlString(value) {
|
||||
if (value === undefined || value === null) return 'null'
|
||||
return `'${String(value).replace(/'/g, "''")}'`
|
||||
}
|
||||
|
||||
function sqlTextArray(values) {
|
||||
if (!values || values.length === 0) return "'{}'"
|
||||
return `'{${values.map((v) => `"${v.replace(/"/g, '\\"')}"`).join(',')}}'`
|
||||
}
|
||||
|
||||
const lines = []
|
||||
lines.push('-- Generated by scripts/generate-seed.mjs from src/domain/library.ts. Do not hand-edit —')
|
||||
lines.push('-- re-run the script after changing the built-in library instead.')
|
||||
lines.push('-- Seeds the public catalog: is_public = true, owner_id = null (system-owned).')
|
||||
lines.push('')
|
||||
|
||||
lines.push('-- Device categories')
|
||||
for (const c of BUILT_IN_DEVICE_CATEGORIES) {
|
||||
lines.push(
|
||||
`insert into public.device_categories (id, name, is_public) values (${sqlString(c.id)}, ${sqlString(c.name)}, true);`,
|
||||
)
|
||||
}
|
||||
lines.push('')
|
||||
|
||||
lines.push('-- Port types')
|
||||
for (const pt of BUILT_IN_PORT_TYPES) {
|
||||
lines.push(
|
||||
`insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values (${sqlString(pt.id)}, ${sqlString(pt.name)}, ${sqlString(pt.category)}, ${sqlString(pt.family)}, ${sqlTextArray(pt.compatibleFamilyIds)}, ${pt.maxConnections ?? 'null'}, true);`,
|
||||
)
|
||||
}
|
||||
lines.push('')
|
||||
|
||||
lines.push('-- Cable types')
|
||||
for (const ct of BUILT_IN_CABLE_TYPES) {
|
||||
lines.push(
|
||||
`insert into public.cable_types (id, name, family, family2, unit, is_public) values (${sqlString(ct.id)}, ${sqlString(ct.name)}, ${sqlString(ct.family)}, ${sqlString(ct.family2)}, ${sqlString(ct.unit)}, true);`,
|
||||
)
|
||||
}
|
||||
lines.push('')
|
||||
|
||||
lines.push('-- Device templates + their ports')
|
||||
for (const dt of BUILT_IN_DEVICE_TEMPLATES) {
|
||||
lines.push(
|
||||
`insert into public.device_templates (id, name, category_id, is_public) values (${sqlString(dt.id)}, ${sqlString(dt.name)}, ${sqlString(dt.category)}, true);`,
|
||||
)
|
||||
for (const [index, port] of dt.ports.entries()) {
|
||||
lines.push(
|
||||
`insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values (${sqlString(randomUUID())}, ${sqlString(dt.id)}, ${sqlString(port.name)}, ${sqlString(port.direction)}, ${sqlString(port.portTypeId)}, ${index});`,
|
||||
)
|
||||
}
|
||||
}
|
||||
lines.push('')
|
||||
|
||||
writeFileSync(new URL('../supabase/seed.sql', import.meta.url), lines.join('\n') + '\n')
|
||||
console.log(`Wrote supabase/seed.sql (${lines.length} lines)`)
|
||||
@@ -0,0 +1,96 @@
|
||||
-- Switch catalog table ids from uuid to text.
|
||||
--
|
||||
-- The app's built-in library (domain/library.ts) has always used stable,
|
||||
-- human-readable string ids ('pt-hdmi', 'dt-display', 'ct-xlr', ...), and
|
||||
-- existing diagrams (including everything created locally so far) reference
|
||||
-- port/cable/category/device types by exactly those ids. Seeding the public
|
||||
-- catalog with fresh uuids instead would silently orphan every one of those
|
||||
-- references. Tables are empty at this point (catalog data hasn't been
|
||||
-- seeded yet), so this is a pure schema change, not a data migration.
|
||||
--
|
||||
-- manufacturers and device_template_ports keep uuid ids — nothing
|
||||
-- referenced manufacturers before now (it's a brand new table), and a
|
||||
-- template's ports were never independently referenced by id (placing a
|
||||
-- device already copies ports with a fresh id, discarding the template
|
||||
-- port's original id — see createDeviceFromTemplate).
|
||||
|
||||
-- Drop FKs that depend on the columns we're about to retype.
|
||||
alter table public.device_template_ports drop constraint device_template_ports_device_template_id_fkey;
|
||||
alter table public.device_template_ports drop constraint device_template_ports_port_type_id_fkey;
|
||||
alter table public.device_templates drop constraint device_templates_category_id_fkey;
|
||||
|
||||
-- device_template_ports' policies join back to device_templates.id, which
|
||||
-- Postgres treats as a dependency that blocks ALTER TYPE on that column —
|
||||
-- drop them here, recreate (verbatim) at the end.
|
||||
drop policy "device_template_ports_select" on public.device_template_ports;
|
||||
drop policy "device_template_ports_insert" on public.device_template_ports;
|
||||
drop policy "device_template_ports_update" on public.device_template_ports;
|
||||
drop policy "device_template_ports_delete" on public.device_template_ports;
|
||||
|
||||
-- Retype the catalog primary keys.
|
||||
alter table public.device_categories alter column id drop default;
|
||||
alter table public.device_categories alter column id type text;
|
||||
|
||||
alter table public.port_types alter column id drop default;
|
||||
alter table public.port_types alter column id type text;
|
||||
|
||||
alter table public.cable_types alter column id drop default;
|
||||
alter table public.cable_types alter column id type text;
|
||||
|
||||
alter table public.device_templates alter column id drop default;
|
||||
alter table public.device_templates alter column id type text;
|
||||
|
||||
-- Retype the columns that reference them.
|
||||
alter table public.device_templates alter column category_id type text;
|
||||
alter table public.device_template_ports alter column device_template_id type text;
|
||||
alter table public.device_template_ports alter column port_type_id type text;
|
||||
|
||||
-- catalog_submissions.entity_id is polymorphic (device_template/port_type/
|
||||
-- cable_type/device_category all now text ids; manufacturer stays uuid, but
|
||||
-- a uuid string is still valid text) — no FK constraint on it to begin with.
|
||||
alter table public.catalog_submissions alter column entity_id type text;
|
||||
|
||||
-- Recreate the FKs.
|
||||
alter table public.device_templates
|
||||
add constraint device_templates_category_id_fkey foreign key (category_id) references public.device_categories (id);
|
||||
alter table public.device_template_ports
|
||||
add constraint device_template_ports_device_template_id_fkey foreign key (device_template_id) references public.device_templates (id) on delete cascade;
|
||||
alter table public.device_template_ports
|
||||
add constraint device_template_ports_port_type_id_fkey foreign key (port_type_id) references public.port_types (id);
|
||||
|
||||
-- Recreate the four policies dropped above, verbatim.
|
||||
create policy "device_template_ports_select" on public.device_template_ports for select
|
||||
using (
|
||||
exists (
|
||||
select 1 from public.device_templates dt
|
||||
where dt.id = device_template_id
|
||||
and (dt.is_public or dt.owner_id = auth.uid() or public.is_admin())
|
||||
)
|
||||
);
|
||||
|
||||
create policy "device_template_ports_insert" on public.device_template_ports for insert
|
||||
with check (
|
||||
exists (
|
||||
select 1 from public.device_templates dt
|
||||
where dt.id = device_template_id
|
||||
and ((dt.owner_id = auth.uid() and not dt.is_public) or public.is_admin())
|
||||
)
|
||||
);
|
||||
|
||||
create policy "device_template_ports_update" on public.device_template_ports for update
|
||||
using (
|
||||
exists (
|
||||
select 1 from public.device_templates dt
|
||||
where dt.id = device_template_id
|
||||
and ((dt.owner_id = auth.uid() and not dt.is_public) or public.is_admin())
|
||||
)
|
||||
);
|
||||
|
||||
create policy "device_template_ports_delete" on public.device_template_ports for delete
|
||||
using (
|
||||
exists (
|
||||
select 1 from public.device_templates dt
|
||||
where dt.id = device_template_id
|
||||
and ((dt.owner_id = auth.uid() and not dt.is_public) or public.is_admin())
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,125 @@
|
||||
-- Generated by scripts/generate-seed.mjs from src/domain/library.ts. Do not hand-edit —
|
||||
-- re-run the script after changing the built-in library instead.
|
||||
-- Seeds the public catalog: is_public = true, owner_id = null (system-owned).
|
||||
|
||||
-- Device categories
|
||||
insert into public.device_categories (id, name, is_public) values ('display', 'Display', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('video-source', 'Video Source', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('audio-source', 'Audio Source', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('synth', 'Synthesizer / Sound Module', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('mixer', 'Mixer', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('amplifier', 'Amplifier / Receiver', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('speaker', 'Speaker', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('network', 'Network', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('computer', 'Computer', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('camera', 'Camera', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('control', 'Control', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('power', 'Power', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('adapter', 'Adapter / Converter', true);
|
||||
insert into public.device_categories (id, name, is_public) values ('other', 'Other', true);
|
||||
|
||||
-- Port types
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-hdmi', 'HDMI', 'video', 'hdmi', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-displayport', 'DisplayPort', 'video', 'displayport', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-sdi', 'SDI (BNC)', 'video', 'sdi', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-vga', 'VGA', 'video', 'vga', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-composite', 'Composite Video (RCA)', 'video', 'rca-composite', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-xlr', 'XLR (Balanced Audio)', 'audio', 'xlr', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-trs-quarter', '1/4" TRS (Balanced Audio)', 'audio', 'trs-quarter', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-ts-quarter', '1/4" TS (Unbalanced Audio)', 'audio', 'ts-quarter', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-trs-mini', '3.5mm TRS (Balanced Audio)', 'audio', 'trs-mini', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-ts-mini', '3.5mm (Unbalanced Audio)', 'audio', 'ts-mini', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-trs-quarter-combo', '1/4" TRS/TS (Balanced or Unbalanced)', 'audio', 'trs-quarter-combo', '{"trs-quarter","ts-quarter"}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-trs-mini-combo', '3.5mm TRS/TS (Balanced or Unbalanced)', 'audio', 'trs-mini-combo', '{"trs-mini","ts-mini"}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-rca-audio', 'RCA (Stereo Audio)', 'audio', 'rca-audio', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-speakon', 'Speakon', 'audio', 'speakon', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-speaker-wire', 'Speaker Wire (Bare/Terminal)', 'audio', 'speaker-wire', '{}', 1, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-toslink', 'TOSLINK (Optical)', 'audio', 'toslink', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-cat-rj45', 'Cat5e/6 (RJ45)', 'network', 'cat-rj45', '{"cat-rj45"}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-fiber-lc', 'Fiber (LC)', 'network', 'fiber-lc', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-usb-a', 'USB-A (Unspecified)', 'usb', 'usb-a', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-usb-c', 'USB-C (Unspecified)', 'usb', 'usb-c', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-usb-a-host', 'USB-A (Host)', 'usb', 'usb-a-host', '{"usb-a-device","usb-a-to-b"}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-usb-a-device', 'USB-A (Device)', 'usb', 'usb-a-device', '{"usb-a-host"}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-usb-c-host', 'USB-C (Host)', 'usb', 'usb-c-host', '{"usb-c-device","usb-c-to-b"}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-usb-c-device', 'USB-C (Device)', 'usb', 'usb-c-device', '{"usb-c-host"}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-usb-b-device', 'USB-B (Device)', 'usb', 'usb-b-device', '{"usb-a-to-b","usb-c-to-b"}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-iec-power', 'IEC C13/C14 Power', 'power', 'iec-power', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-nema-power', 'NEMA 5-15 Power', 'power', 'nema-power', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-rs232', 'RS-232 (Serial)', 'control', 'rs232', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-ir', 'IR (3.5mm Emitter/Receiver)', 'control', 'ir', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-midi-din', 'MIDI (5-Pin DIN)', 'control', 'midi-din', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-midi-trs-a', '3.5mm MIDI (Type A)', 'control', 'midi-trs-a', '{}', null, true);
|
||||
insert into public.port_types (id, name, category, family, compatible_family_ids, max_connections, is_public) values ('pt-midi-trs-b', '3.5mm MIDI (Type B)', 'control', 'midi-trs-b', '{}', null, true);
|
||||
|
||||
-- Cable types
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-hdmi', 'HDMI Cable', 'hdmi', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-displayport', 'DisplayPort Cable', 'displayport', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-sdi', 'SDI Coax (BNC)', 'sdi', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-vga', 'VGA Cable', 'vga', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-composite', 'Composite Video Cable (RCA)', 'rca-composite', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-xlr', 'XLR Cable', 'xlr', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-xlr-to-trs-quarter', 'XLR to 1/4" TRS Adapter Cable', 'xlr', 'trs-quarter', 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-trs-quarter', '1/4" TRS Cable (Balanced)', 'trs-quarter', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-ts-quarter', '1/4" TS Cable (Instrument/Unbalanced)', 'ts-quarter', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-trs-mini', '3.5mm TRS Cable (Balanced)', 'trs-mini', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-ts-mini', '3.5mm Cable (Unbalanced)', 'ts-mini', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-rca-audio', 'RCA Stereo Audio Cable', 'rca-audio', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-speakon', 'Speakon Cable', 'speakon', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-speaker-wire', 'Speaker Wire (14/2)', 'speaker-wire', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-toslink', 'TOSLINK Optical Cable', 'toslink', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-cat6', 'Cat6 Cable', 'cat-rj45', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-fiber-lc', 'Fiber LC-LC Cable', 'fiber-lc', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-usb-a', 'USB-A to USB-A Cable', 'usb-a', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-usb-c', 'USB-C Cable', 'usb-c', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-usb-a-host-device', 'USB-A Cable (Host ↔ Device)', 'usb-a-host', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-usb-c-host-device', 'USB-C Cable (Host ↔ Device)', 'usb-c-host', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-usb-a-to-b', 'USB-A to USB-B Cable', 'usb-a-to-b', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-usb-c-to-b', 'USB-C to USB-B Cable', 'usb-c-to-b', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-iec-power', 'IEC C13 Power Cable', 'iec-power', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-nema-power', 'NEMA 5-15 Extension Cord', 'nema-power', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-rs232', 'RS-232 Serial Cable', 'rs232', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-ir', 'IR Emitter Cable', 'ir', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-midi-din', 'MIDI Cable (5-Pin DIN)', 'midi-din', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-midi-trs-a', '3.5mm MIDI Cable (Type A)', 'midi-trs-a', null, 'ft', true);
|
||||
insert into public.cable_types (id, name, family, family2, unit, is_public) values ('ct-midi-trs-b', '3.5mm MIDI Cable (Type B)', 'midi-trs-b', null, 'ft', true);
|
||||
|
||||
-- Device templates + their ports
|
||||
insert into public.device_templates (id, name, category_id, is_public) values ('dt-display', 'Display (TV / Projector)', 'display', true);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('62079818-e050-4dee-8065-4eedd40f4c02', 'dt-display', 'HDMI In 1', 'input', 'pt-hdmi', 0);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('04eeee4d-ac8e-4aef-a2ae-1e4dd2e4fc2a', 'dt-display', 'HDMI In 2', 'input', 'pt-hdmi', 1);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('182dd666-cfe7-489a-97e4-78e2fdf91668', 'dt-display', 'Power In', 'input', 'pt-iec-power', 2);
|
||||
insert into public.device_templates (id, name, category_id, is_public) values ('dt-media-player', 'Media Player / Streamer', 'video-source', true);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('21d1e934-948b-4060-afc1-8f8827223ed7', 'dt-media-player', 'HDMI Out', 'output', 'pt-hdmi', 0);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('e9123870-9f39-4703-9d2c-56cff2bf5162', 'dt-media-player', 'Power In', 'input', 'pt-nema-power', 1);
|
||||
insert into public.device_templates (id, name, category_id, is_public) values ('dt-avr', 'AV Receiver', 'amplifier', true);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('1ab3b4b4-51db-46bf-b473-42203cc522d9', 'dt-avr', 'HDMI In 1', 'input', 'pt-hdmi', 0);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('66536c92-a719-46a1-bb5e-4e15b4e3aa15', 'dt-avr', 'HDMI In 2', 'input', 'pt-hdmi', 1);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('6b4611a2-f2ec-409f-a89d-97bfa70fdd1d', 'dt-avr', 'HDMI In 3', 'input', 'pt-hdmi', 2);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('345d6b0e-d260-4b7c-be62-cadd581890ec', 'dt-avr', 'HDMI Out (Display)', 'output', 'pt-hdmi', 3);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('07d68503-7f75-4783-be3f-db34b3066f83', 'dt-avr', 'Speaker Out L', 'output', 'pt-speaker-wire', 4);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('f68bf47e-dd17-4b64-84d9-ada7b3b75132', 'dt-avr', 'Speaker Out R', 'output', 'pt-speaker-wire', 5);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('7c6780fd-a26c-4c09-ab6c-5417ed9a31de', 'dt-avr', 'Power In', 'input', 'pt-iec-power', 6);
|
||||
insert into public.device_templates (id, name, category_id, is_public) values ('dt-speaker', 'Speaker (Passive)', 'speaker', true);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('3a0157b0-635a-43cb-b874-446c70ab32f2', 'dt-speaker', 'Speaker In', 'input', 'pt-speaker-wire', 0);
|
||||
insert into public.device_templates (id, name, category_id, is_public) values ('dt-switch', 'Network Switch', 'network', true);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('8167e698-3693-4256-aa84-80de3638710c', 'dt-switch', 'Port 1', 'bidirectional', 'pt-cat-rj45', 0);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('ce4e8e83-0c91-422d-88df-6caf6af5a145', 'dt-switch', 'Port 2', 'bidirectional', 'pt-cat-rj45', 1);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('f215e7ab-2c12-46c9-989e-ee6d55c2fa1f', 'dt-switch', 'Port 3', 'bidirectional', 'pt-cat-rj45', 2);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('baf456fc-643a-42e4-8981-ce22eefa94b3', 'dt-switch', 'Port 4', 'bidirectional', 'pt-cat-rj45', 3);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('42c14e32-7315-4008-a8d9-32621e4d9569', 'dt-switch', 'Power In', 'input', 'pt-iec-power', 4);
|
||||
insert into public.device_templates (id, name, category_id, is_public) values ('dt-camera', 'PTZ Camera', 'camera', true);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('83e946f2-2ad3-4e31-889d-fccddf2bebde', 'dt-camera', 'SDI Out', 'output', 'pt-sdi', 0);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('439abff2-ff23-4bc1-b096-6f67e9a6fc32', 'dt-camera', 'Control (RJ45)', 'bidirectional', 'pt-cat-rj45', 1);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('ea9355fc-3668-4085-9787-5439d9485a04', 'dt-camera', 'Power In', 'input', 'pt-nema-power', 2);
|
||||
insert into public.device_templates (id, name, category_id, is_public) values ('dt-computer', 'Computer / Laptop', 'computer', true);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('c8bd2521-5227-419c-a437-d319cf4f90ba', 'dt-computer', 'HDMI Out', 'output', 'pt-hdmi', 0);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('73c8c3fb-9ee7-4f39-a241-449bf5ce36ea', 'dt-computer', 'USB-C', 'bidirectional', 'pt-usb-c', 1);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('e417fea4-acf6-44a2-848f-a479003a1ee1', 'dt-computer', 'Ethernet', 'bidirectional', 'pt-cat-rj45', 2);
|
||||
insert into public.device_templates (id, name, category_id, is_public) values ('dt-adapter-midi-trs-a-to-din', '3.5mm-to-DIN MIDI Adapter (Type A)', 'adapter', true);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('5b295fb4-290f-4800-ba5f-4ad2b314717a', 'dt-adapter-midi-trs-a-to-din', '3.5mm In (from MIDI Out)', 'input', 'pt-midi-trs-a', 0);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('cdfb5cf4-848f-4389-bacd-9858f85ce6fd', 'dt-adapter-midi-trs-a-to-din', 'DIN Out (to MIDI In)', 'output', 'pt-midi-din', 1);
|
||||
insert into public.device_templates (id, name, category_id, is_public) values ('dt-adapter-midi-trs-b-to-din', '3.5mm-to-DIN MIDI Adapter (Type B)', 'adapter', true);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('c1f2e564-8b14-483e-a719-633ffa1901e3', 'dt-adapter-midi-trs-b-to-din', '3.5mm In (from MIDI Out)', 'input', 'pt-midi-trs-b', 0);
|
||||
insert into public.device_template_ports (id, device_template_id, name, direction, port_type_id, sort_order) values ('8b5d138d-d936-47a9-a625-dafc184c1e7f', 'dt-adapter-midi-trs-b-to-din', 'DIN Out (to MIDI In)', 'output', 'pt-midi-din', 1);
|
||||
|
||||
Reference in New Issue
Block a user