Add administrator sign-in and private-safe room directory
Connect personal operator-provisioned admin keys, persisted roles and eight-hour sessions, current/all-session revocation and a filtered room directory. Additive schema protects final owner, revokes disabled accounts and records safe audit history. No game state changes; remaining administration scope stays explicit. Verified: typecheck, lint, 5196 offline tests, 52 HTTP tests, production build; authenticated local HTTP and desktop/mobile browser checks. Independent privacy/security review fixed malformed nested roster searches and read-time session revocation. Preserved 1,671 prior rooms and 3,538 seats. Container/deployed acceptance follows publication.
This commit is contained in:
1 parent
36a03bda9c
commit
c2d392e155
27 files changed
+3164
-13
No files matched your search
@@ -0,0 +1,83 @@
|
||||
CREATE TABLE `admin_accounts` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`role` text NOT NULL,
|
||||
`key_hash` text NOT NULL,
|
||||
`enabled` integer DEFAULT 1 NOT NULL,
|
||||
`session_generation` integer DEFAULT 0 NOT NULL,
|
||||
`created_at` integer NOT NULL,
|
||||
`updated_at` integer NOT NULL,
|
||||
CONSTRAINT "admin_accounts_role" CHECK("admin_accounts"."role" IN ('owner', 'operator', 'viewer')),
|
||||
CONSTRAINT "admin_accounts_enabled" CHECK("admin_accounts"."enabled" IN (0, 1)),
|
||||
CONSTRAINT "admin_accounts_generation" CHECK("admin_accounts"."session_generation" >= 0)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `admin_accounts_key_hash_unique` ON `admin_accounts` (`key_hash`);--> statement-breakpoint
|
||||
CREATE TABLE `admin_audit` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`actor_admin_id` text,
|
||||
`target_admin_id` text NOT NULL,
|
||||
`action` text NOT NULL,
|
||||
`created_at` integer NOT NULL,
|
||||
`detail` text DEFAULT '{}' NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE INDEX `admin_audit_created_at` ON `admin_audit` (`created_at`);--> statement-breakpoint
|
||||
CREATE TABLE `admin_sessions` (
|
||||
`token_hash` text PRIMARY KEY NOT NULL,
|
||||
`id` text NOT NULL,
|
||||
`admin_id` text NOT NULL,
|
||||
`generation` integer NOT NULL,
|
||||
`created_at` integer NOT NULL,
|
||||
`expires_at` integer NOT NULL,
|
||||
`revoked_at` integer,
|
||||
FOREIGN KEY (`admin_id`) REFERENCES `admin_accounts`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `admin_sessions_id_unique` ON `admin_sessions` (`id`);--> statement-breakpoint
|
||||
CREATE INDEX `admin_sessions_account` ON `admin_sessions` (`admin_id`);--> statement-breakpoint
|
||||
CREATE INDEX `rooms_updated_at` ON `rooms` (`updated_at`);
|
||||
--> statement-breakpoint
|
||||
-- D1 operator SQL must be atomic without client-managed BEGIN/COMMIT.
|
||||
-- Drizzle records the table schema; these SQLite triggers retain the account
|
||||
-- audit and revocation invariants for every writer, including the setup CLI.
|
||||
CREATE TRIGGER `admin_accounts_provision_audit`
|
||||
AFTER INSERT ON `admin_accounts`
|
||||
BEGIN
|
||||
INSERT INTO admin_audit (id, actor_admin_id, target_admin_id, action, created_at, detail)
|
||||
VALUES (lower(hex(randomblob(16))), NULL, NEW.id, 'provision', unixepoch() * 1000,
|
||||
json_object('role', NEW.role));
|
||||
END;
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER `admin_accounts_last_owner_update`
|
||||
BEFORE UPDATE OF enabled, role ON `admin_accounts`
|
||||
WHEN OLD.enabled = 1 AND OLD.role = 'owner'
|
||||
AND (NEW.enabled != 1 OR NEW.role != 'owner')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM admin_accounts WHERE id != OLD.id AND enabled = 1 AND role = 'owner'
|
||||
)
|
||||
BEGIN
|
||||
SELECT RAISE(ABORT, 'Cannot remove the final enabled administrator owner.');
|
||||
END;
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER `admin_accounts_last_owner_delete`
|
||||
BEFORE DELETE ON `admin_accounts`
|
||||
WHEN OLD.enabled = 1 AND OLD.role = 'owner'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM admin_accounts WHERE id != OLD.id AND enabled = 1 AND role = 'owner'
|
||||
)
|
||||
BEGIN
|
||||
SELECT RAISE(ABORT, 'Cannot remove the final enabled administrator owner.');
|
||||
END;
|
||||
--> statement-breakpoint
|
||||
CREATE TRIGGER `admin_accounts_revoke`
|
||||
AFTER UPDATE OF enabled ON `admin_accounts`
|
||||
WHEN OLD.enabled = 1 AND NEW.enabled = 0
|
||||
BEGIN
|
||||
UPDATE admin_accounts SET session_generation = session_generation + 1 WHERE id = NEW.id;
|
||||
UPDATE admin_sessions SET revoked_at = unixepoch() * 1000
|
||||
WHERE admin_id = NEW.id AND revoked_at IS NULL;
|
||||
INSERT INTO admin_audit (id, actor_admin_id, target_admin_id, action, created_at, detail)
|
||||
VALUES (lower(hex(randomblob(16))), NULL, NEW.id, 'revoke', unixepoch() * 1000,
|
||||
json_object('previousRole', OLD.role));
|
||||
END;
|
||||
@@ -0,0 +1,982 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "cc440330-962f-474a-8bf1-25a2609c6e9a",
|
||||
"prevId": "9d40c7aa-918d-4da7-adc8-d6d8dc49d1c5",
|
||||
"tables": {
|
||||
"admin_accounts": {
|
||||
"name": "admin_accounts",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key_hash": {
|
||||
"name": "key_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"enabled": {
|
||||
"name": "enabled",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 1
|
||||
},
|
||||
"session_generation": {
|
||||
"name": "session_generation",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"admin_accounts_key_hash_unique": {
|
||||
"name": "admin_accounts_key_hash_unique",
|
||||
"columns": [
|
||||
"key_hash"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {
|
||||
"admin_accounts_role": {
|
||||
"name": "admin_accounts_role",
|
||||
"value": "\"admin_accounts\".\"role\" IN ('owner', 'operator', 'viewer')"
|
||||
},
|
||||
"admin_accounts_enabled": {
|
||||
"name": "admin_accounts_enabled",
|
||||
"value": "\"admin_accounts\".\"enabled\" IN (0, 1)"
|
||||
},
|
||||
"admin_accounts_generation": {
|
||||
"name": "admin_accounts_generation",
|
||||
"value": "\"admin_accounts\".\"session_generation\" >= 0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"admin_audit": {
|
||||
"name": "admin_audit",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"actor_admin_id": {
|
||||
"name": "actor_admin_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"target_admin_id": {
|
||||
"name": "target_admin_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"action": {
|
||||
"name": "action",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"detail": {
|
||||
"name": "detail",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'{}'"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"admin_audit_created_at": {
|
||||
"name": "admin_audit_created_at",
|
||||
"columns": [
|
||||
"created_at"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"admin_sessions": {
|
||||
"name": "admin_sessions",
|
||||
"columns": {
|
||||
"token_hash": {
|
||||
"name": "token_hash",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"admin_id": {
|
||||
"name": "admin_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"generation": {
|
||||
"name": "generation",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"revoked_at": {
|
||||
"name": "revoked_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"admin_sessions_id_unique": {
|
||||
"name": "admin_sessions_id_unique",
|
||||
"columns": [
|
||||
"id"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"admin_sessions_account": {
|
||||
"name": "admin_sessions_account",
|
||||
"columns": [
|
||||
"admin_id"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"admin_sessions_admin_id_admin_accounts_id_fk": {
|
||||
"name": "admin_sessions_admin_id_admin_accounts_id_fk",
|
||||
"tableFrom": "admin_sessions",
|
||||
"tableTo": "admin_accounts",
|
||||
"columnsFrom": [
|
||||
"admin_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"room_entry_receipts": {
|
||||
"name": "room_entry_receipts",
|
||||
"columns": {
|
||||
"operation_hash": {
|
||||
"name": "operation_hash",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"request_hash": {
|
||||
"name": "request_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"session_hash": {
|
||||
"name": "session_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"room_code": {
|
||||
"name": "room_code",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"player_id": {
|
||||
"name": "player_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"room_entry_receipts_room_code_rooms_code_fk": {
|
||||
"name": "room_entry_receipts_room_code_rooms_code_fk",
|
||||
"tableFrom": "room_entry_receipts",
|
||||
"tableTo": "rooms",
|
||||
"columnsFrom": [
|
||||
"room_code"
|
||||
],
|
||||
"columnsTo": [
|
||||
"code"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"room_messages": {
|
||||
"name": "room_messages",
|
||||
"columns": {
|
||||
"sequence": {
|
||||
"name": "sequence",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"room_code": {
|
||||
"name": "room_code",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"sender_id": {
|
||||
"name": "sender_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"sender_session_hash": {
|
||||
"name": "sender_session_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"sender_name": {
|
||||
"name": "sender_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"sender_faction": {
|
||||
"name": "sender_faction",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"recipient_id": {
|
||||
"name": "recipient_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"recipient_name": {
|
||||
"name": "recipient_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"body": {
|
||||
"name": "body",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"room_messages_room_id": {
|
||||
"name": "room_messages_room_id",
|
||||
"columns": [
|
||||
"room_code",
|
||||
"id"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"room_messages_channel": {
|
||||
"name": "room_messages_channel",
|
||||
"columns": [
|
||||
"room_code",
|
||||
"recipient_id",
|
||||
"sequence"
|
||||
],
|
||||
"isUnique": false
|
||||
},
|
||||
"room_messages_sender": {
|
||||
"name": "room_messages_sender",
|
||||
"columns": [
|
||||
"room_code",
|
||||
"sender_id",
|
||||
"created_at"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"room_messages_room_code_rooms_code_fk": {
|
||||
"name": "room_messages_room_code_rooms_code_fk",
|
||||
"tableFrom": "room_messages",
|
||||
"tableTo": "rooms",
|
||||
"columnsFrom": [
|
||||
"room_code"
|
||||
],
|
||||
"columnsTo": [
|
||||
"code"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"rooms": {
|
||||
"name": "rooms",
|
||||
"columns": {
|
||||
"code": {
|
||||
"name": "code",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"state": {
|
||||
"name": "state",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"version": {
|
||||
"name": "version",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"rooms_updated_at": {
|
||||
"name": "rooms_updated_at",
|
||||
"columns": [
|
||||
"updated_at"
|
||||
],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"seat_ai_delegations": {
|
||||
"name": "seat_ai_delegations",
|
||||
"columns": {
|
||||
"room_code": {
|
||||
"name": "room_code",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"owner_id": {
|
||||
"name": "owner_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"grant_id": {
|
||||
"name": "grant_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"delegate_id": {
|
||||
"name": "delegate_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"difficulty": {
|
||||
"name": "difficulty",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"owner_session_hash": {
|
||||
"name": "owner_session_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"delegate_session_hash": {
|
||||
"name": "delegate_session_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"used_at": {
|
||||
"name": "used_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"revoked_at": {
|
||||
"name": "revoked_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"seat_ai_delegations_grant_id_unique": {
|
||||
"name": "seat_ai_delegations_grant_id_unique",
|
||||
"columns": [
|
||||
"grant_id"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"seat_ai_delegations_room_code_rooms_code_fk": {
|
||||
"name": "seat_ai_delegations_room_code_rooms_code_fk",
|
||||
"tableFrom": "seat_ai_delegations",
|
||||
"tableTo": "rooms",
|
||||
"columnsFrom": [
|
||||
"room_code"
|
||||
],
|
||||
"columnsTo": [
|
||||
"code"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"seat_ai_delegations_room_code_owner_id_grant_id_pk": {
|
||||
"columns": [
|
||||
"room_code",
|
||||
"owner_id",
|
||||
"grant_id"
|
||||
],
|
||||
"name": "seat_ai_delegations_room_code_owner_id_grant_id_pk"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"seat_handover_claim_receipts": {
|
||||
"name": "seat_handover_claim_receipts",
|
||||
"columns": {
|
||||
"room_code": {
|
||||
"name": "room_code",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"player_id": {
|
||||
"name": "player_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"operation_hash": {
|
||||
"name": "operation_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"offer_hash": {
|
||||
"name": "offer_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"secret_hash": {
|
||||
"name": "secret_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"session_hash": {
|
||||
"name": "session_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"claim_fence": {
|
||||
"name": "claim_fence",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"claimed_at": {
|
||||
"name": "claimed_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"seat_handover_claim_receipts_claim_fence_unique": {
|
||||
"name": "seat_handover_claim_receipts_claim_fence_unique",
|
||||
"columns": [
|
||||
"claim_fence"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"seat_handover_claim_receipts_room_code_rooms_code_fk": {
|
||||
"name": "seat_handover_claim_receipts_room_code_rooms_code_fk",
|
||||
"tableFrom": "seat_handover_claim_receipts",
|
||||
"tableTo": "rooms",
|
||||
"columnsFrom": [
|
||||
"room_code"
|
||||
],
|
||||
"columnsTo": [
|
||||
"code"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"seat_handover_claim_receipts_room_code_player_id_operation_hash_pk": {
|
||||
"columns": [
|
||||
"room_code",
|
||||
"player_id",
|
||||
"operation_hash"
|
||||
],
|
||||
"name": "seat_handover_claim_receipts_room_code_player_id_operation_hash_pk"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"seat_handover_offers": {
|
||||
"name": "seat_handover_offers",
|
||||
"columns": {
|
||||
"room_code": {
|
||||
"name": "room_code",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"player_id": {
|
||||
"name": "player_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"offer_hash": {
|
||||
"name": "offer_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"secret_hash": {
|
||||
"name": "secret_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"issuer_session_hash": {
|
||||
"name": "issuer_session_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"claim_operation_hash": {
|
||||
"name": "claim_operation_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"session_hash": {
|
||||
"name": "session_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"claimed_at": {
|
||||
"name": "claimed_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"seat_handover_offers_offer_hash_unique": {
|
||||
"name": "seat_handover_offers_offer_hash_unique",
|
||||
"columns": [
|
||||
"offer_hash"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"seat_handover_offers_room_code_rooms_code_fk": {
|
||||
"name": "seat_handover_offers_room_code_rooms_code_fk",
|
||||
"tableFrom": "seat_handover_offers",
|
||||
"tableTo": "rooms",
|
||||
"columnsFrom": [
|
||||
"room_code"
|
||||
],
|
||||
"columnsTo": [
|
||||
"code"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"seat_handover_offers_room_code_player_id_pk": {
|
||||
"columns": [
|
||||
"room_code",
|
||||
"player_id"
|
||||
],
|
||||
"name": "seat_handover_offers_room_code_player_id_pk"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"seat_recovery_keys": {
|
||||
"name": "seat_recovery_keys",
|
||||
"columns": {
|
||||
"room_code": {
|
||||
"name": "room_code",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"player_id": {
|
||||
"name": "player_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"recovery_hash": {
|
||||
"name": "recovery_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"current_operation_hash": {
|
||||
"name": "current_operation_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"seat_recovery_keys_room_code_rooms_code_fk": {
|
||||
"name": "seat_recovery_keys_room_code_rooms_code_fk",
|
||||
"tableFrom": "seat_recovery_keys",
|
||||
"tableTo": "rooms",
|
||||
"columnsFrom": [
|
||||
"room_code"
|
||||
],
|
||||
"columnsTo": [
|
||||
"code"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"seat_recovery_keys_room_code_player_id_pk": {
|
||||
"columns": [
|
||||
"room_code",
|
||||
"player_id"
|
||||
],
|
||||
"name": "seat_recovery_keys_room_code_player_id_pk"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"seat_recovery_receipts": {
|
||||
"name": "seat_recovery_receipts",
|
||||
"columns": {
|
||||
"room_code": {
|
||||
"name": "room_code",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"player_id": {
|
||||
"name": "player_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"operation_hash": {
|
||||
"name": "operation_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"recovery_hash": {
|
||||
"name": "recovery_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"session_hash": {
|
||||
"name": "session_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"seat_recovery_receipts_room_code_rooms_code_fk": {
|
||||
"name": "seat_recovery_receipts_room_code_rooms_code_fk",
|
||||
"tableFrom": "seat_recovery_receipts",
|
||||
"tableTo": "rooms",
|
||||
"columnsFrom": [
|
||||
"room_code"
|
||||
],
|
||||
"columnsTo": [
|
||||
"code"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"seat_recovery_receipts_room_code_player_id_operation_hash_pk": {
|
||||
"columns": [
|
||||
"room_code",
|
||||
"player_id",
|
||||
"operation_hash"
|
||||
],
|
||||
"name": "seat_recovery_receipts_room_code_player_id_operation_hash_pk"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"seats": {
|
||||
"name": "seats",
|
||||
"columns": {
|
||||
"token_hash": {
|
||||
"name": "token_hash",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"revoked": {
|
||||
"name": "revoked",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"room_code": {
|
||||
"name": "room_code",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"player_id": {
|
||||
"name": "player_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"seats_room_code_rooms_code_fk": {
|
||||
"name": "seats_room_code_rooms_code_fk",
|
||||
"tableFrom": "seats",
|
||||
"tableTo": "rooms",
|
||||
"columnsFrom": [
|
||||
"room_code"
|
||||
],
|
||||
"columnsTo": [
|
||||
"code"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,13 @@
|
||||
"when": 1789947127493,
|
||||
"tag": "0007_clammy_sprite",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "6",
|
||||
"when": 1790032690330,
|
||||
"tag": "0008_admin_access",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user