Stop storing printers' access codes
`printers.access_code` held a printer's LAN password - the same value Bambu uses for MQTT and as the FTPS password. It was writable through `POST /printers`, excluded from `printerProjection` so it could not leave the server, and **never read by any backend code path**. A secret at rest in the cloud, serving no purpose. The credential belongs on the proxy, which is the only thing that uses it. Under the three-tier architecture it goes FE -> API -> proxy, with the API relaying it rather than persisting it. Considered and rejected, deliberately: keeping the column so the console could show an operator their printer's access code. Useful, but not worth the risk. Removing the column is what makes that rejection real - a stored secret is one a future route can leak, and the projection was the only thing standing between this column and a response. `PRINTER_WRITABLE` and the two schemas derived from it move to `db/validators.ts`, which already declares itself the single place these shapes are defined. The reason is testability: a route imports `db/index.ts` and so needs a live `DATABASE_URL`, and "what may a client set" is exactly the kind of thing worth asserting with no database in sight. Four tests pin the decision so it is enforced rather than remembered, because a column is easy to re-add and the symptom would be silent. One of them records a hole rather than a fix: `metadata` is a free-form jsonb column that is both writable and in the public projection, so a well-meaning client can still stash a credential there and have it stored and served back. Removing the dedicated column narrowed the door; it did not close it. The e2e script still submits `accessCode: "SECRET123"` on purpose. The field is now unknown to the create schema, the schema is open, and Elysia cleans it away rather than rejecting the request - so the canary proves against a real server that a client which tries to hand the cloud a credential has it dropped and never echoed. That is runtime behaviour the unit test can only establish at the schema level. Migration 0003 is a single `DROP COLUMN IF EXISTS`, applied locally. There is no production, staging or dev environment and nothing was writing the column, so there is no data to preserve and no consumer to break.
This commit is contained in:
@@ -0,0 +1,709 @@
|
||||
{
|
||||
"id": "e4751490-d3cf-44ca-8807-f7dba03415ea",
|
||||
"prevId": "95497aac-876c-40e4-9683-f7a177fa5177",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.org_members": {
|
||||
"name": "org_members",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"org_id": {
|
||||
"name": "org_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "org_role",
|
||||
"typeSchema": "public",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'viewer'"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"org_members_user_id_idx": {
|
||||
"name": "org_members_user_id_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "user_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"org_members_org_id_organisations_id_fk": {
|
||||
"name": "org_members_org_id_organisations_id_fk",
|
||||
"tableFrom": "org_members",
|
||||
"tableTo": "organisations",
|
||||
"columnsFrom": [
|
||||
"org_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"org_members_org_id_user_id_pk": {
|
||||
"name": "org_members_org_id_user_id_pk",
|
||||
"columns": [
|
||||
"org_id",
|
||||
"user_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"public.organisations": {
|
||||
"name": "organisations",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"settings": {
|
||||
"name": "settings",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"public.farms": {
|
||||
"name": "farms",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"org_id": {
|
||||
"name": "org_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"edge_proxy_token": {
|
||||
"name": "edge_proxy_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"location": {
|
||||
"name": "location",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"settings": {
|
||||
"name": "settings",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"farms_org_id_idx": {
|
||||
"name": "farms_org_id_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "org_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"farms_org_id_organisations_id_fk": {
|
||||
"name": "farms_org_id_organisations_id_fk",
|
||||
"tableFrom": "farms",
|
||||
"tableTo": "organisations",
|
||||
"columnsFrom": [
|
||||
"org_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"public.printers": {
|
||||
"name": "printers",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"farm_id": {
|
||||
"name": "farm_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"vendor": {
|
||||
"name": "vendor",
|
||||
"type": "printer_vendor",
|
||||
"typeSchema": "public",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "printer_status",
|
||||
"typeSchema": "public",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'offline'"
|
||||
},
|
||||
"lan_address": {
|
||||
"name": "lan_address",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"serial_number": {
|
||||
"name": "serial_number",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"gateway_printer_id": {
|
||||
"name": "gateway_printer_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"nozzle_temp_c": {
|
||||
"name": "nozzle_temp_c",
|
||||
"type": "real",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"nozzle_target_temp_c": {
|
||||
"name": "nozzle_target_temp_c",
|
||||
"type": "real",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bed_temp_c": {
|
||||
"name": "bed_temp_c",
|
||||
"type": "real",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bed_target_temp_c": {
|
||||
"name": "bed_target_temp_c",
|
||||
"type": "real",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"progress_percent": {
|
||||
"name": "progress_percent",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"current_layer": {
|
||||
"name": "current_layer",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"total_layers": {
|
||||
"name": "total_layers",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"remaining_minutes": {
|
||||
"name": "remaining_minutes",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"current_job_name": {
|
||||
"name": "current_job_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"active_alerts": {
|
||||
"name": "active_alerts",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"camera_stream": {
|
||||
"name": "camera_stream",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"vendor_state": {
|
||||
"name": "vendor_state",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"vendor_error_code": {
|
||||
"name": "vendor_error_code",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"metadata": {
|
||||
"name": "metadata",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"last_seen_at": {
|
||||
"name": "last_seen_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"printers_farm_gateway_printer_id_idx": {
|
||||
"name": "printers_farm_gateway_printer_id_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "farm_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
},
|
||||
{
|
||||
"expression": "gateway_printer_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"printers_farm_id_farms_id_fk": {
|
||||
"name": "printers_farm_id_farms_id_fk",
|
||||
"tableFrom": "printers",
|
||||
"tableTo": "farms",
|
||||
"columnsFrom": [
|
||||
"farm_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"public.print_jobs": {
|
||||
"name": "print_jobs",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"farm_id": {
|
||||
"name": "farm_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"printer_id": {
|
||||
"name": "printer_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"file_name": {
|
||||
"name": "file_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"r2_object_key": {
|
||||
"name": "r2_object_key",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "print_job_status",
|
||||
"typeSchema": "public",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'queued'"
|
||||
},
|
||||
"progress_pct": {
|
||||
"name": "progress_pct",
|
||||
"type": "real",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": 0
|
||||
},
|
||||
"estimated_time_s": {
|
||||
"name": "estimated_time_s",
|
||||
"type": "real",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"elapsed_time_s": {
|
||||
"name": "elapsed_time_s",
|
||||
"type": "real",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"submitted_by": {
|
||||
"name": "submitted_by",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"metadata": {
|
||||
"name": "metadata",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"started_at": {
|
||||
"name": "started_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"completed_at": {
|
||||
"name": "completed_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"print_jobs_farm_id_farms_id_fk": {
|
||||
"name": "print_jobs_farm_id_farms_id_fk",
|
||||
"tableFrom": "print_jobs",
|
||||
"tableTo": "farms",
|
||||
"columnsFrom": [
|
||||
"farm_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"print_jobs_printer_id_printers_id_fk": {
|
||||
"name": "print_jobs_printer_id_printers_id_fk",
|
||||
"tableFrom": "print_jobs",
|
||||
"tableTo": "printers",
|
||||
"columnsFrom": [
|
||||
"printer_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"public.frame_embeddings": {
|
||||
"name": "frame_embeddings",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"print_job_id": {
|
||||
"name": "print_job_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"printer_id": {
|
||||
"name": "printer_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"r2_object_key": {
|
||||
"name": "r2_object_key",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"embedding": {
|
||||
"name": "embedding",
|
||||
"type": "vector(512)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"captured_at": {
|
||||
"name": "captured_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"frame_embeddings_embedding_ivfflat_idx": {
|
||||
"name": "frame_embeddings_embedding_ivfflat_idx",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "embedding",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last",
|
||||
"opclass": "vector_cosine_ops"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "ivfflat",
|
||||
"with": {
|
||||
"lists": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"frame_embeddings_print_job_id_print_jobs_id_fk": {
|
||||
"name": "frame_embeddings_print_job_id_print_jobs_id_fk",
|
||||
"tableFrom": "frame_embeddings",
|
||||
"tableTo": "print_jobs",
|
||||
"columnsFrom": [
|
||||
"print_job_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"frame_embeddings_printer_id_printers_id_fk": {
|
||||
"name": "frame_embeddings_printer_id_printers_id_fk",
|
||||
"tableFrom": "frame_embeddings",
|
||||
"tableTo": "printers",
|
||||
"columnsFrom": [
|
||||
"printer_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
}
|
||||
},
|
||||
"enums": {
|
||||
"public.org_role": {
|
||||
"name": "org_role",
|
||||
"schema": "public",
|
||||
"values": [
|
||||
"viewer",
|
||||
"operator",
|
||||
"owner"
|
||||
]
|
||||
},
|
||||
"public.printer_status": {
|
||||
"name": "printer_status",
|
||||
"schema": "public",
|
||||
"values": [
|
||||
"offline",
|
||||
"idle",
|
||||
"printing",
|
||||
"paused",
|
||||
"error",
|
||||
"maintenance"
|
||||
]
|
||||
},
|
||||
"public.printer_vendor": {
|
||||
"name": "printer_vendor",
|
||||
"schema": "public",
|
||||
"values": [
|
||||
"bambu",
|
||||
"prusa",
|
||||
"klipper"
|
||||
]
|
||||
},
|
||||
"public.print_job_status": {
|
||||
"name": "print_job_status",
|
||||
"schema": "public",
|
||||
"values": [
|
||||
"queued",
|
||||
"uploading",
|
||||
"printing",
|
||||
"paused",
|
||||
"completed",
|
||||
"failed",
|
||||
"cancelled"
|
||||
]
|
||||
}
|
||||
},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user