the universal sandbox runtime for agents and humans. pocketenv.io
sandbox openclaw agent claude-code vercel-sandbox deno-sandbox cloudflare-sandbox atproto sprites daytona
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add name field to sandbox secrets and variables

Add unique (sandbox_id, name) indexes, DB migration, and update API
insertions to store the name. Add a useNotyf hook, include Notyf CSS,
and add minor CSS tweaks and imports in modal components.

+1177 -39
+5 -1
apps/api/src/schema/sandbox-secrets.ts
··· 15 15 secretId: text("secret_id") 16 16 .notNull() 17 17 .references(() => secrets.id), 18 + name: text("name"), 18 19 }, 19 - (t) => [uniqueIndex("unique_sandbox_secret").on(t.sandboxId, t.secretId)], 20 + (t) => [ 21 + uniqueIndex("unique_sandbox_secret").on(t.sandboxId, t.secretId), 22 + uniqueIndex("unique_sandbox_secret_by_name").on(t.sandboxId, t.name), 23 + ], 20 24 ); 21 25 22 26 export type SelectSandboxSecret = InferSelectModel<typeof sandboxSecrets>;
+2
apps/api/src/schema/sandbox-variables.ts
··· 15 15 variableId: text("variable_id") 16 16 .notNull() 17 17 .references(() => variables.id), 18 + name: text("name"), 18 19 }, 19 20 (t) => [ 20 21 uniqueIndex("unique_sandbox_variables").on(t.sandboxId, t.variableId), 22 + uniqueIndex("unique_sandbox_variables_by_name").on(t.sandboxId, t.name), 21 23 ], 22 24 ); 23 25
+1
apps/api/src/xrpc/io/pocketenv/secret/addSecret.ts
··· 34 34 .values({ 35 35 secretId: secret.id, 36 36 sandboxId: input.body.secret.sandboxId, 37 + name: input.body.secret.name, 37 38 }) 38 39 .returning() 39 40 .execute();
+1
apps/api/src/xrpc/io/pocketenv/variable/addVariable.ts
··· 34 34 .values({ 35 35 sandboxId: input.body.variable.sandboxId, 36 36 variableId: variable.id, 37 + name: input.body.variable.name, 37 38 }) 38 39 .execute(); 39 40 });
+4
apps/cf-sandbox/drizzle/0016_milky_invaders.sql
··· 1 + ALTER TABLE "sandbox_secrets" ADD COLUMN "name" text;--> statement-breakpoint 2 + ALTER TABLE "sandbox_variables" ADD COLUMN "name" text;--> statement-breakpoint 3 + CREATE UNIQUE INDEX "unique_sandbox_secret_by_name" ON "sandbox_secrets" USING btree ("sandbox_id","name");--> statement-breakpoint 4 + CREATE UNIQUE INDEX "unique_sandbox_variables_by_name" ON "sandbox_variables" USING btree ("sandbox_id","name");
+1008
apps/cf-sandbox/drizzle/meta/0016_snapshot.json
··· 1 + { 2 + "id": "4ed922ed-167d-4b31-858e-db2bf8e6787a", 3 + "prevId": "328548c9-a059-4546-9135-0d66f4d3f606", 4 + "version": "7", 5 + "dialect": "postgresql", 6 + "tables": { 7 + "public.authorized_keys": { 8 + "name": "authorized_keys", 9 + "schema": "", 10 + "columns": { 11 + "id": { 12 + "name": "id", 13 + "type": "text", 14 + "primaryKey": true, 15 + "notNull": true, 16 + "default": "xata_id()" 17 + }, 18 + "sandbox_id": { 19 + "name": "sandbox_id", 20 + "type": "text", 21 + "primaryKey": false, 22 + "notNull": false 23 + }, 24 + "public_key": { 25 + "name": "public_key", 26 + "type": "text", 27 + "primaryKey": false, 28 + "notNull": true 29 + }, 30 + "created_at": { 31 + "name": "created_at", 32 + "type": "timestamp", 33 + "primaryKey": false, 34 + "notNull": true, 35 + "default": "now()" 36 + } 37 + }, 38 + "indexes": {}, 39 + "foreignKeys": { 40 + "authorized_keys_sandbox_id_sandboxes_id_fk": { 41 + "name": "authorized_keys_sandbox_id_sandboxes_id_fk", 42 + "tableFrom": "authorized_keys", 43 + "tableTo": "sandboxes", 44 + "columnsFrom": [ 45 + "sandbox_id" 46 + ], 47 + "columnsTo": [ 48 + "id" 49 + ], 50 + "onDelete": "no action", 51 + "onUpdate": "no action" 52 + } 53 + }, 54 + "compositePrimaryKeys": {}, 55 + "uniqueConstraints": {}, 56 + "policies": {}, 57 + "checkConstraints": {}, 58 + "isRLSEnabled": false 59 + }, 60 + "public.files": { 61 + "name": "files", 62 + "schema": "", 63 + "columns": { 64 + "id": { 65 + "name": "id", 66 + "type": "text", 67 + "primaryKey": true, 68 + "notNull": true, 69 + "default": "variable_id()" 70 + }, 71 + "content": { 72 + "name": "content", 73 + "type": "text", 74 + "primaryKey": false, 75 + "notNull": true 76 + }, 77 + "created_at": { 78 + "name": "created_at", 79 + "type": "timestamp", 80 + "primaryKey": false, 81 + "notNull": true, 82 + "default": "now()" 83 + }, 84 + "updated_at": { 85 + "name": "updated_at", 86 + "type": "timestamp", 87 + "primaryKey": false, 88 + "notNull": true, 89 + "default": "now()" 90 + } 91 + }, 92 + "indexes": {}, 93 + "foreignKeys": {}, 94 + "compositePrimaryKeys": {}, 95 + "uniqueConstraints": {}, 96 + "policies": {}, 97 + "checkConstraints": {}, 98 + "isRLSEnabled": false 99 + }, 100 + "public.sandbox_files": { 101 + "name": "sandbox_files", 102 + "schema": "", 103 + "columns": { 104 + "id": { 105 + "name": "id", 106 + "type": "text", 107 + "primaryKey": true, 108 + "notNull": true, 109 + "default": "xata_id()" 110 + }, 111 + "sandbox_id": { 112 + "name": "sandbox_id", 113 + "type": "text", 114 + "primaryKey": false, 115 + "notNull": true 116 + }, 117 + "file_id": { 118 + "name": "file_id", 119 + "type": "text", 120 + "primaryKey": false, 121 + "notNull": true 122 + }, 123 + "path": { 124 + "name": "path", 125 + "type": "text", 126 + "primaryKey": false, 127 + "notNull": true 128 + } 129 + }, 130 + "indexes": { 131 + "unique_sandbox_file": { 132 + "name": "unique_sandbox_file", 133 + "columns": [ 134 + { 135 + "expression": "sandbox_id", 136 + "isExpression": false, 137 + "asc": true, 138 + "nulls": "last" 139 + }, 140 + { 141 + "expression": "file_id", 142 + "isExpression": false, 143 + "asc": true, 144 + "nulls": "last" 145 + } 146 + ], 147 + "isUnique": true, 148 + "concurrently": false, 149 + "method": "btree", 150 + "with": {} 151 + }, 152 + "unique_sandbox_file_path": { 153 + "name": "unique_sandbox_file_path", 154 + "columns": [ 155 + { 156 + "expression": "sandbox_id", 157 + "isExpression": false, 158 + "asc": true, 159 + "nulls": "last" 160 + }, 161 + { 162 + "expression": "path", 163 + "isExpression": false, 164 + "asc": true, 165 + "nulls": "last" 166 + } 167 + ], 168 + "isUnique": true, 169 + "concurrently": false, 170 + "method": "btree", 171 + "with": {} 172 + } 173 + }, 174 + "foreignKeys": { 175 + "sandbox_files_sandbox_id_sandboxes_id_fk": { 176 + "name": "sandbox_files_sandbox_id_sandboxes_id_fk", 177 + "tableFrom": "sandbox_files", 178 + "tableTo": "sandboxes", 179 + "columnsFrom": [ 180 + "sandbox_id" 181 + ], 182 + "columnsTo": [ 183 + "id" 184 + ], 185 + "onDelete": "no action", 186 + "onUpdate": "no action" 187 + }, 188 + "sandbox_files_file_id_files_id_fk": { 189 + "name": "sandbox_files_file_id_files_id_fk", 190 + "tableFrom": "sandbox_files", 191 + "tableTo": "files", 192 + "columnsFrom": [ 193 + "file_id" 194 + ], 195 + "columnsTo": [ 196 + "id" 197 + ], 198 + "onDelete": "no action", 199 + "onUpdate": "no action" 200 + } 201 + }, 202 + "compositePrimaryKeys": {}, 203 + "uniqueConstraints": {}, 204 + "policies": {}, 205 + "checkConstraints": {}, 206 + "isRLSEnabled": false 207 + }, 208 + "public.sandbox_secrets": { 209 + "name": "sandbox_secrets", 210 + "schema": "", 211 + "columns": { 212 + "id": { 213 + "name": "id", 214 + "type": "text", 215 + "primaryKey": true, 216 + "notNull": true, 217 + "default": "xata_id()" 218 + }, 219 + "sandbox_id": { 220 + "name": "sandbox_id", 221 + "type": "text", 222 + "primaryKey": false, 223 + "notNull": true 224 + }, 225 + "secret_id": { 226 + "name": "secret_id", 227 + "type": "text", 228 + "primaryKey": false, 229 + "notNull": true 230 + }, 231 + "name": { 232 + "name": "name", 233 + "type": "text", 234 + "primaryKey": false, 235 + "notNull": false 236 + } 237 + }, 238 + "indexes": { 239 + "unique_sandbox_secret": { 240 + "name": "unique_sandbox_secret", 241 + "columns": [ 242 + { 243 + "expression": "sandbox_id", 244 + "isExpression": false, 245 + "asc": true, 246 + "nulls": "last" 247 + }, 248 + { 249 + "expression": "secret_id", 250 + "isExpression": false, 251 + "asc": true, 252 + "nulls": "last" 253 + } 254 + ], 255 + "isUnique": true, 256 + "concurrently": false, 257 + "method": "btree", 258 + "with": {} 259 + }, 260 + "unique_sandbox_secret_by_name": { 261 + "name": "unique_sandbox_secret_by_name", 262 + "columns": [ 263 + { 264 + "expression": "sandbox_id", 265 + "isExpression": false, 266 + "asc": true, 267 + "nulls": "last" 268 + }, 269 + { 270 + "expression": "name", 271 + "isExpression": false, 272 + "asc": true, 273 + "nulls": "last" 274 + } 275 + ], 276 + "isUnique": true, 277 + "concurrently": false, 278 + "method": "btree", 279 + "with": {} 280 + } 281 + }, 282 + "foreignKeys": { 283 + "sandbox_secrets_sandbox_id_sandboxes_id_fk": { 284 + "name": "sandbox_secrets_sandbox_id_sandboxes_id_fk", 285 + "tableFrom": "sandbox_secrets", 286 + "tableTo": "sandboxes", 287 + "columnsFrom": [ 288 + "sandbox_id" 289 + ], 290 + "columnsTo": [ 291 + "id" 292 + ], 293 + "onDelete": "no action", 294 + "onUpdate": "no action" 295 + }, 296 + "sandbox_secrets_secret_id_secrets_id_fk": { 297 + "name": "sandbox_secrets_secret_id_secrets_id_fk", 298 + "tableFrom": "sandbox_secrets", 299 + "tableTo": "secrets", 300 + "columnsFrom": [ 301 + "secret_id" 302 + ], 303 + "columnsTo": [ 304 + "id" 305 + ], 306 + "onDelete": "no action", 307 + "onUpdate": "no action" 308 + } 309 + }, 310 + "compositePrimaryKeys": {}, 311 + "uniqueConstraints": {}, 312 + "policies": {}, 313 + "checkConstraints": {}, 314 + "isRLSEnabled": false 315 + }, 316 + "public.sandbox_variables": { 317 + "name": "sandbox_variables", 318 + "schema": "", 319 + "columns": { 320 + "id": { 321 + "name": "id", 322 + "type": "text", 323 + "primaryKey": true, 324 + "notNull": true, 325 + "default": "xata_id()" 326 + }, 327 + "sandbox_id": { 328 + "name": "sandbox_id", 329 + "type": "text", 330 + "primaryKey": false, 331 + "notNull": true 332 + }, 333 + "variable_id": { 334 + "name": "variable_id", 335 + "type": "text", 336 + "primaryKey": false, 337 + "notNull": true 338 + }, 339 + "name": { 340 + "name": "name", 341 + "type": "text", 342 + "primaryKey": false, 343 + "notNull": false 344 + } 345 + }, 346 + "indexes": { 347 + "unique_sandbox_variables": { 348 + "name": "unique_sandbox_variables", 349 + "columns": [ 350 + { 351 + "expression": "sandbox_id", 352 + "isExpression": false, 353 + "asc": true, 354 + "nulls": "last" 355 + }, 356 + { 357 + "expression": "variable_id", 358 + "isExpression": false, 359 + "asc": true, 360 + "nulls": "last" 361 + } 362 + ], 363 + "isUnique": true, 364 + "concurrently": false, 365 + "method": "btree", 366 + "with": {} 367 + }, 368 + "unique_sandbox_variables_by_name": { 369 + "name": "unique_sandbox_variables_by_name", 370 + "columns": [ 371 + { 372 + "expression": "sandbox_id", 373 + "isExpression": false, 374 + "asc": true, 375 + "nulls": "last" 376 + }, 377 + { 378 + "expression": "name", 379 + "isExpression": false, 380 + "asc": true, 381 + "nulls": "last" 382 + } 383 + ], 384 + "isUnique": true, 385 + "concurrently": false, 386 + "method": "btree", 387 + "with": {} 388 + } 389 + }, 390 + "foreignKeys": { 391 + "sandbox_variables_sandbox_id_sandboxes_id_fk": { 392 + "name": "sandbox_variables_sandbox_id_sandboxes_id_fk", 393 + "tableFrom": "sandbox_variables", 394 + "tableTo": "sandboxes", 395 + "columnsFrom": [ 396 + "sandbox_id" 397 + ], 398 + "columnsTo": [ 399 + "id" 400 + ], 401 + "onDelete": "no action", 402 + "onUpdate": "no action" 403 + }, 404 + "sandbox_variables_variable_id_variables_id_fk": { 405 + "name": "sandbox_variables_variable_id_variables_id_fk", 406 + "tableFrom": "sandbox_variables", 407 + "tableTo": "variables", 408 + "columnsFrom": [ 409 + "variable_id" 410 + ], 411 + "columnsTo": [ 412 + "id" 413 + ], 414 + "onDelete": "no action", 415 + "onUpdate": "no action" 416 + } 417 + }, 418 + "compositePrimaryKeys": {}, 419 + "uniqueConstraints": {}, 420 + "policies": {}, 421 + "checkConstraints": {}, 422 + "isRLSEnabled": false 423 + }, 424 + "public.sandbox_volumes": { 425 + "name": "sandbox_volumes", 426 + "schema": "", 427 + "columns": { 428 + "id": { 429 + "name": "id", 430 + "type": "text", 431 + "primaryKey": true, 432 + "notNull": true, 433 + "default": "xata_id()" 434 + }, 435 + "sandbox_id": { 436 + "name": "sandbox_id", 437 + "type": "text", 438 + "primaryKey": false, 439 + "notNull": true 440 + }, 441 + "volume_id": { 442 + "name": "volume_id", 443 + "type": "text", 444 + "primaryKey": false, 445 + "notNull": true 446 + }, 447 + "path": { 448 + "name": "path", 449 + "type": "text", 450 + "primaryKey": false, 451 + "notNull": true 452 + } 453 + }, 454 + "indexes": { 455 + "unique_sandbox_volume": { 456 + "name": "unique_sandbox_volume", 457 + "columns": [ 458 + { 459 + "expression": "sandbox_id", 460 + "isExpression": false, 461 + "asc": true, 462 + "nulls": "last" 463 + }, 464 + { 465 + "expression": "volume_id", 466 + "isExpression": false, 467 + "asc": true, 468 + "nulls": "last" 469 + } 470 + ], 471 + "isUnique": true, 472 + "concurrently": false, 473 + "method": "btree", 474 + "with": {} 475 + }, 476 + "unique_sandbox_volume_path": { 477 + "name": "unique_sandbox_volume_path", 478 + "columns": [ 479 + { 480 + "expression": "sandbox_id", 481 + "isExpression": false, 482 + "asc": true, 483 + "nulls": "last" 484 + }, 485 + { 486 + "expression": "path", 487 + "isExpression": false, 488 + "asc": true, 489 + "nulls": "last" 490 + } 491 + ], 492 + "isUnique": true, 493 + "concurrently": false, 494 + "method": "btree", 495 + "with": {} 496 + } 497 + }, 498 + "foreignKeys": { 499 + "sandbox_volumes_sandbox_id_sandboxes_id_fk": { 500 + "name": "sandbox_volumes_sandbox_id_sandboxes_id_fk", 501 + "tableFrom": "sandbox_volumes", 502 + "tableTo": "sandboxes", 503 + "columnsFrom": [ 504 + "sandbox_id" 505 + ], 506 + "columnsTo": [ 507 + "id" 508 + ], 509 + "onDelete": "no action", 510 + "onUpdate": "no action" 511 + }, 512 + "sandbox_volumes_volume_id_volumes_id_fk": { 513 + "name": "sandbox_volumes_volume_id_volumes_id_fk", 514 + "tableFrom": "sandbox_volumes", 515 + "tableTo": "volumes", 516 + "columnsFrom": [ 517 + "volume_id" 518 + ], 519 + "columnsTo": [ 520 + "id" 521 + ], 522 + "onDelete": "no action", 523 + "onUpdate": "no action" 524 + } 525 + }, 526 + "compositePrimaryKeys": {}, 527 + "uniqueConstraints": {}, 528 + "policies": {}, 529 + "checkConstraints": {}, 530 + "isRLSEnabled": false 531 + }, 532 + "public.sandboxes": { 533 + "name": "sandboxes", 534 + "schema": "", 535 + "columns": { 536 + "id": { 537 + "name": "id", 538 + "type": "text", 539 + "primaryKey": true, 540 + "notNull": true, 541 + "default": "sandbox_id()" 542 + }, 543 + "base": { 544 + "name": "base", 545 + "type": "text", 546 + "primaryKey": false, 547 + "notNull": false 548 + }, 549 + "name": { 550 + "name": "name", 551 + "type": "text", 552 + "primaryKey": false, 553 + "notNull": true 554 + }, 555 + "display_name": { 556 + "name": "display_name", 557 + "type": "text", 558 + "primaryKey": false, 559 + "notNull": false 560 + }, 561 + "uri": { 562 + "name": "uri", 563 + "type": "text", 564 + "primaryKey": false, 565 + "notNull": false 566 + }, 567 + "cid": { 568 + "name": "cid", 569 + "type": "text", 570 + "primaryKey": false, 571 + "notNull": false 572 + }, 573 + "repo": { 574 + "name": "repo", 575 + "type": "text", 576 + "primaryKey": false, 577 + "notNull": false 578 + }, 579 + "provider": { 580 + "name": "provider", 581 + "type": "text", 582 + "primaryKey": false, 583 + "notNull": true, 584 + "default": "'cloudflare'" 585 + }, 586 + "description": { 587 + "name": "description", 588 + "type": "text", 589 + "primaryKey": false, 590 + "notNull": false 591 + }, 592 + "logo": { 593 + "name": "logo", 594 + "type": "text", 595 + "primaryKey": false, 596 + "notNull": false 597 + }, 598 + "readme": { 599 + "name": "readme", 600 + "type": "text", 601 + "primaryKey": false, 602 + "notNull": false 603 + }, 604 + "public_key": { 605 + "name": "public_key", 606 + "type": "text", 607 + "primaryKey": false, 608 + "notNull": true 609 + }, 610 + "user_id": { 611 + "name": "user_id", 612 + "type": "text", 613 + "primaryKey": false, 614 + "notNull": false 615 + }, 616 + "instance_type": { 617 + "name": "instance_type", 618 + "type": "text", 619 + "primaryKey": false, 620 + "notNull": false 621 + }, 622 + "vcpus": { 623 + "name": "vcpus", 624 + "type": "integer", 625 + "primaryKey": false, 626 + "notNull": false 627 + }, 628 + "memory": { 629 + "name": "memory", 630 + "type": "integer", 631 + "primaryKey": false, 632 + "notNull": false 633 + }, 634 + "disk": { 635 + "name": "disk", 636 + "type": "integer", 637 + "primaryKey": false, 638 + "notNull": false 639 + }, 640 + "status": { 641 + "name": "status", 642 + "type": "text", 643 + "primaryKey": false, 644 + "notNull": true 645 + }, 646 + "keep_alive": { 647 + "name": "keep_alive", 648 + "type": "boolean", 649 + "primaryKey": false, 650 + "notNull": true, 651 + "default": false 652 + }, 653 + "sleep_after": { 654 + "name": "sleep_after", 655 + "type": "text", 656 + "primaryKey": false, 657 + "notNull": false 658 + }, 659 + "sandbox_id": { 660 + "name": "sandbox_id", 661 + "type": "text", 662 + "primaryKey": false, 663 + "notNull": false 664 + }, 665 + "installs": { 666 + "name": "installs", 667 + "type": "integer", 668 + "primaryKey": false, 669 + "notNull": true, 670 + "default": 0 671 + }, 672 + "started_at": { 673 + "name": "started_at", 674 + "type": "timestamp", 675 + "primaryKey": false, 676 + "notNull": false 677 + }, 678 + "created_at": { 679 + "name": "created_at", 680 + "type": "timestamp", 681 + "primaryKey": false, 682 + "notNull": true, 683 + "default": "now()" 684 + }, 685 + "updated_at": { 686 + "name": "updated_at", 687 + "type": "timestamp", 688 + "primaryKey": false, 689 + "notNull": true, 690 + "default": "now()" 691 + } 692 + }, 693 + "indexes": {}, 694 + "foreignKeys": { 695 + "sandboxes_user_id_users_id_fk": { 696 + "name": "sandboxes_user_id_users_id_fk", 697 + "tableFrom": "sandboxes", 698 + "tableTo": "users", 699 + "columnsFrom": [ 700 + "user_id" 701 + ], 702 + "columnsTo": [ 703 + "id" 704 + ], 705 + "onDelete": "no action", 706 + "onUpdate": "no action" 707 + } 708 + }, 709 + "compositePrimaryKeys": {}, 710 + "uniqueConstraints": { 711 + "sandboxes_name_unique": { 712 + "name": "sandboxes_name_unique", 713 + "nullsNotDistinct": false, 714 + "columns": [ 715 + "name" 716 + ] 717 + }, 718 + "sandboxes_uri_unique": { 719 + "name": "sandboxes_uri_unique", 720 + "nullsNotDistinct": false, 721 + "columns": [ 722 + "uri" 723 + ] 724 + }, 725 + "sandboxes_cid_unique": { 726 + "name": "sandboxes_cid_unique", 727 + "nullsNotDistinct": false, 728 + "columns": [ 729 + "cid" 730 + ] 731 + } 732 + }, 733 + "policies": {}, 734 + "checkConstraints": {}, 735 + "isRLSEnabled": false 736 + }, 737 + "public.secrets": { 738 + "name": "secrets", 739 + "schema": "", 740 + "columns": { 741 + "id": { 742 + "name": "id", 743 + "type": "text", 744 + "primaryKey": true, 745 + "notNull": true, 746 + "default": "secret_id()" 747 + }, 748 + "name": { 749 + "name": "name", 750 + "type": "text", 751 + "primaryKey": false, 752 + "notNull": true 753 + }, 754 + "value": { 755 + "name": "value", 756 + "type": "text", 757 + "primaryKey": false, 758 + "notNull": true 759 + }, 760 + "created_at": { 761 + "name": "created_at", 762 + "type": "timestamp", 763 + "primaryKey": false, 764 + "notNull": true, 765 + "default": "now()" 766 + } 767 + }, 768 + "indexes": {}, 769 + "foreignKeys": {}, 770 + "compositePrimaryKeys": {}, 771 + "uniqueConstraints": {}, 772 + "policies": {}, 773 + "checkConstraints": {}, 774 + "isRLSEnabled": false 775 + }, 776 + "public.snapshots": { 777 + "name": "snapshots", 778 + "schema": "", 779 + "columns": { 780 + "id": { 781 + "name": "id", 782 + "type": "text", 783 + "primaryKey": true, 784 + "notNull": true, 785 + "default": "snapshot_id()" 786 + }, 787 + "slug": { 788 + "name": "slug", 789 + "type": "text", 790 + "primaryKey": false, 791 + "notNull": true 792 + }, 793 + "created_at": { 794 + "name": "created_at", 795 + "type": "timestamp", 796 + "primaryKey": false, 797 + "notNull": true, 798 + "default": "now()" 799 + } 800 + }, 801 + "indexes": {}, 802 + "foreignKeys": {}, 803 + "compositePrimaryKeys": {}, 804 + "uniqueConstraints": { 805 + "snapshots_slug_unique": { 806 + "name": "snapshots_slug_unique", 807 + "nullsNotDistinct": false, 808 + "columns": [ 809 + "slug" 810 + ] 811 + } 812 + }, 813 + "policies": {}, 814 + "checkConstraints": {}, 815 + "isRLSEnabled": false 816 + }, 817 + "public.users": { 818 + "name": "users", 819 + "schema": "", 820 + "columns": { 821 + "id": { 822 + "name": "id", 823 + "type": "text", 824 + "primaryKey": true, 825 + "notNull": true, 826 + "default": "xata_id()" 827 + }, 828 + "did": { 829 + "name": "did", 830 + "type": "text", 831 + "primaryKey": false, 832 + "notNull": true 833 + }, 834 + "display_name": { 835 + "name": "display_name", 836 + "type": "text", 837 + "primaryKey": false, 838 + "notNull": false 839 + }, 840 + "handle": { 841 + "name": "handle", 842 + "type": "text", 843 + "primaryKey": false, 844 + "notNull": true 845 + }, 846 + "avatar": { 847 + "name": "avatar", 848 + "type": "text", 849 + "primaryKey": false, 850 + "notNull": false 851 + }, 852 + "created_at": { 853 + "name": "created_at", 854 + "type": "timestamp", 855 + "primaryKey": false, 856 + "notNull": true, 857 + "default": "now()" 858 + }, 859 + "updated_at": { 860 + "name": "updated_at", 861 + "type": "timestamp", 862 + "primaryKey": false, 863 + "notNull": true, 864 + "default": "now()" 865 + } 866 + }, 867 + "indexes": {}, 868 + "foreignKeys": {}, 869 + "compositePrimaryKeys": {}, 870 + "uniqueConstraints": { 871 + "users_did_unique": { 872 + "name": "users_did_unique", 873 + "nullsNotDistinct": false, 874 + "columns": [ 875 + "did" 876 + ] 877 + }, 878 + "users_handle_unique": { 879 + "name": "users_handle_unique", 880 + "nullsNotDistinct": false, 881 + "columns": [ 882 + "handle" 883 + ] 884 + } 885 + }, 886 + "policies": {}, 887 + "checkConstraints": {}, 888 + "isRLSEnabled": false 889 + }, 890 + "public.variables": { 891 + "name": "variables", 892 + "schema": "", 893 + "columns": { 894 + "id": { 895 + "name": "id", 896 + "type": "text", 897 + "primaryKey": true, 898 + "notNull": true, 899 + "default": "variable_id()" 900 + }, 901 + "name": { 902 + "name": "name", 903 + "type": "text", 904 + "primaryKey": false, 905 + "notNull": true 906 + }, 907 + "value": { 908 + "name": "value", 909 + "type": "text", 910 + "primaryKey": false, 911 + "notNull": true 912 + }, 913 + "created_at": { 914 + "name": "created_at", 915 + "type": "timestamp", 916 + "primaryKey": false, 917 + "notNull": true, 918 + "default": "now()" 919 + }, 920 + "updated_at": { 921 + "name": "updated_at", 922 + "type": "timestamp", 923 + "primaryKey": false, 924 + "notNull": true, 925 + "default": "now()" 926 + } 927 + }, 928 + "indexes": {}, 929 + "foreignKeys": {}, 930 + "compositePrimaryKeys": {}, 931 + "uniqueConstraints": {}, 932 + "policies": {}, 933 + "checkConstraints": {}, 934 + "isRLSEnabled": false 935 + }, 936 + "public.volumes": { 937 + "name": "volumes", 938 + "schema": "", 939 + "columns": { 940 + "id": { 941 + "name": "id", 942 + "type": "text", 943 + "primaryKey": true, 944 + "notNull": true, 945 + "default": "volume_id()" 946 + }, 947 + "slug": { 948 + "name": "slug", 949 + "type": "text", 950 + "primaryKey": false, 951 + "notNull": true 952 + }, 953 + "size": { 954 + "name": "size", 955 + "type": "integer", 956 + "primaryKey": false, 957 + "notNull": true 958 + }, 959 + "size_unit": { 960 + "name": "size_unit", 961 + "type": "text", 962 + "primaryKey": false, 963 + "notNull": true 964 + }, 965 + "created_at": { 966 + "name": "created_at", 967 + "type": "timestamp", 968 + "primaryKey": false, 969 + "notNull": true, 970 + "default": "now()" 971 + }, 972 + "updated_at": { 973 + "name": "updated_at", 974 + "type": "timestamp", 975 + "primaryKey": false, 976 + "notNull": true, 977 + "default": "now()" 978 + } 979 + }, 980 + "indexes": {}, 981 + "foreignKeys": {}, 982 + "compositePrimaryKeys": {}, 983 + "uniqueConstraints": { 984 + "volumes_slug_unique": { 985 + "name": "volumes_slug_unique", 986 + "nullsNotDistinct": false, 987 + "columns": [ 988 + "slug" 989 + ] 990 + } 991 + }, 992 + "policies": {}, 993 + "checkConstraints": {}, 994 + "isRLSEnabled": false 995 + } 996 + }, 997 + "enums": {}, 998 + "schemas": {}, 999 + "sequences": {}, 1000 + "roles": {}, 1001 + "policies": {}, 1002 + "views": {}, 1003 + "_meta": { 1004 + "columns": {}, 1005 + "schemas": {}, 1006 + "tables": {} 1007 + } 1008 + }
+7
apps/cf-sandbox/drizzle/meta/_journal.json
··· 113 113 "when": 1772855509416, 114 114 "tag": "0015_yellow_lester", 115 115 "breakpoints": true 116 + }, 117 + { 118 + "idx": 16, 119 + "version": "7", 120 + "when": 1772858827451, 121 + "tag": "0016_milky_invaders", 122 + "breakpoints": true 116 123 } 117 124 ] 118 125 }
+5 -1
apps/cf-sandbox/src/schema/sandbox-secrets.ts
··· 15 15 secretId: text("secret_id") 16 16 .notNull() 17 17 .references(() => secrets.id), 18 + name: text("name"), 18 19 }, 19 - (t) => [uniqueIndex("unique_sandbox_secret").on(t.sandboxId, t.secretId)], 20 + (t) => [ 21 + uniqueIndex("unique_sandbox_secret").on(t.sandboxId, t.secretId), 22 + uniqueIndex("unique_sandbox_secret_by_name").on(t.sandboxId, t.name), 23 + ], 20 24 ); 21 25 22 26 export type SelectSandboxSecret = InferSelectModel<typeof sandboxSecrets>;
+2
apps/cf-sandbox/src/schema/sandbox-variables.ts
··· 15 15 variableId: text("variable_id") 16 16 .notNull() 17 17 .references(() => variables.id), 18 + name: text("name"), 18 19 }, 19 20 (t) => [ 20 21 uniqueIndex("unique_sandbox_variables").on(t.sandboxId, t.variableId), 22 + uniqueIndex("unique_sandbox_variables_by_name").on(t.sandboxId, t.name), 21 23 ], 22 24 ); 23 25
+5 -1
apps/sandbox/src/schema/sandbox-secrets.ts
··· 15 15 secretId: text("secret_id") 16 16 .notNull() 17 17 .references(() => secrets.id), 18 + name: text("name"), 18 19 }, 19 - (t) => [uniqueIndex("unique_sandbox_secret").on(t.sandboxId, t.secretId)], 20 + (t) => [ 21 + uniqueIndex("unique_sandbox_secret").on(t.sandboxId, t.secretId), 22 + uniqueIndex("unique_sandbox_secret_by_name").on(t.sandboxId, t.name), 23 + ], 20 24 ); 21 25 22 26 export type SelectSandboxSecret = InferSelectModel<typeof sandboxSecrets>;
+2
apps/sandbox/src/schema/sandbox-variables.ts
··· 15 15 variableId: text("variable_id") 16 16 .notNull() 17 17 .references(() => variables.id), 18 + name: text("name"), 18 19 }, 19 20 (t) => [ 20 21 uniqueIndex("unique_sandbox_variables").on(t.sandboxId, t.variableId), 22 + uniqueIndex("unique_sandbox_variables_by_name").on(t.sandboxId, t.name), 21 23 ], 22 24 ); 23 25
+19 -9
apps/web/src/components/contextmenu/AddEnvironmentVariableModal/AddEnvironmentVariableModal.tsx
··· 4 4 import { z } from "zod"; 5 5 import { zodResolver } from "@hookform/resolvers/zod"; 6 6 import { useAddVariableMutation } from "../../../hooks/useVariable"; 7 + import { useNotyf } from "../../../hooks/useNotyf"; 7 8 8 9 const schema = z.object({ 9 10 name: z.string().min(1, "Name is required"), ··· 24 25 sandboxId, 25 26 }: AddEnvironmentVariableModalProps) { 26 27 const [isLoading, setIsLoading] = useState(false); 28 + const notyf = useNotyf(); 27 29 const { mutateAsync: addVariable } = useAddVariableMutation(); 28 30 const { 29 31 register, ··· 67 69 68 70 const onSubmit = async (data: FormValues) => { 69 71 setIsLoading(true); 70 - await addVariable({ 71 - sandboxId, 72 - name: data.name, 73 - value: data.value, 74 - }); 75 - setIsLoading(false); 76 - reset(); 77 - reset(); 78 - onClose(); 72 + try { 73 + await addVariable({ 74 + sandboxId, 75 + name: data.name, 76 + value: data.value, 77 + }); 78 + setIsLoading(false); 79 + reset(); 80 + onClose(); 81 + notyf.open("primary", "Variable added successfully!"); 82 + } catch { 83 + notyf.open("error", "Failed to add variable!"); 84 + setIsLoading(false); 85 + reset(); 86 + onClose(); 87 + return; 88 + } 79 89 }; 80 90 81 91 if (!isOpen) return null;
+22 -11
apps/web/src/components/contextmenu/AddFileModal/AddFileModal.tsx
··· 6 6 import { useAddFileMutation } from "../../../hooks/useFile"; 7 7 import { useSodium } from "../../../hooks/useSodium"; 8 8 import { PUBLIC_KEY } from "../../../consts"; 9 + import { useNotyf } from "../../../hooks/useNotyf"; 9 10 10 11 const schema = z.object({ 11 12 path: z.string().min(1, "Path is required"), ··· 22 23 23 24 function AddFileModal({ isOpen, onClose, sandboxId }: AddFileModalProps) { 24 25 const sodium = useSodium(); 26 + const notyf = useNotyf(); 25 27 const [isLoading, setIsLoading] = useState(false); 26 28 const { mutateAsync: addFile } = useAddFileMutation(); 27 29 const { ··· 71 73 sodium.fromString(data.content), 72 74 sodium.fromHex(PUBLIC_KEY), 73 75 ); 74 - await addFile({ 75 - sandboxId, 76 - path: data.path, 77 - content: sodium.toBase64( 78 - sealed, 79 - sodium.base64Variants.URLSAFE_NO_PADDING, 80 - ), 81 - }); 82 - setIsLoading(false); 83 - reset(); 84 - onClose(); 76 + try { 77 + await addFile({ 78 + sandboxId, 79 + path: data.path, 80 + content: sodium.toBase64( 81 + sealed, 82 + sodium.base64Variants.URLSAFE_NO_PADDING, 83 + ), 84 + }); 85 + setIsLoading(false); 86 + reset(); 87 + onClose(); 88 + notyf.open("primary", "File added successfully!"); 89 + } catch { 90 + notyf.open("error", "Failed to add file!"); 91 + setIsLoading(false); 92 + reset(); 93 + onClose(); 94 + return; 95 + } 85 96 }; 86 97 87 98 if (!isOpen) return null;
+21 -8
apps/web/src/components/contextmenu/AddSecretModal/AddSecretModal.tsx
··· 6 6 import { useAddSecretMutation } from "../../../hooks/useSecret"; 7 7 import { useSodium } from "../../../hooks/useSodium"; 8 8 import { PUBLIC_KEY } from "../../../consts"; 9 + import { useNotyf } from "../../../hooks/useNotyf"; 9 10 10 11 const schema = z.object({ 11 12 name: z.string().min(1, "Name is required"), ··· 22 23 23 24 function AddSecretModal({ isOpen, onClose, sandboxId }: AddSecretModalProps) { 24 25 const sodium = useSodium(); 26 + const notyf = useNotyf(); 25 27 const [isLoading, setIsLoading] = useState(false); 26 28 const { mutateAsync: addSecret } = useAddSecretMutation(); 27 29 const { ··· 71 73 sodium.fromString(data.value), 72 74 sodium.fromHex(PUBLIC_KEY), 73 75 ); 74 - await addSecret({ 75 - sandboxId, 76 - name: data.name, 77 - value: sodium.toBase64(sealed, sodium.base64Variants.URLSAFE_NO_PADDING), 78 - }); 79 - setIsLoading(false); 80 - reset(); 81 - onClose(); 76 + try { 77 + await addSecret({ 78 + sandboxId, 79 + name: data.name, 80 + value: sodium.toBase64( 81 + sealed, 82 + sodium.base64Variants.URLSAFE_NO_PADDING, 83 + ), 84 + }); 85 + setIsLoading(false); 86 + reset(); 87 + onClose(); 88 + notyf.open("primary", "Secret added successfully!"); 89 + } catch { 90 + notyf.open("error", "Failed to add secret!"); 91 + setIsLoading(false); 92 + reset(); 93 + onClose(); 94 + } 82 95 }; 83 96 84 97 if (!isOpen) return null;
+18 -8
apps/web/src/components/contextmenu/AddVolumeModal/AddVolumeModal.tsx
··· 4 4 import { z } from "zod"; 5 5 import { zodResolver } from "@hookform/resolvers/zod"; 6 6 import { useAddVolumeMutation } from "../../../hooks/useVolume"; 7 + import { useNotyf } from "../../../hooks/useNotyf"; 7 8 8 9 const schema = z.object({ 9 10 name: z.string().min(1, "Name is required"), ··· 19 20 }; 20 21 21 22 function AddVolumeModal({ isOpen, onClose, sandboxId }: AddVolumeModalProps) { 23 + const notyf = useNotyf(); 22 24 const [isLoading, setIsLoading] = useState(false); 23 25 const { mutateAsync: addVolume } = useAddVolumeMutation(); 24 26 const { ··· 64 66 65 67 const onSubmit = async (data: FormValues) => { 66 68 setIsLoading(true); 67 - await addVolume({ 68 - sandboxId, 69 - name: data.name, 70 - path: data.path, 71 - }); 72 - setIsLoading(false); 73 - reset(); 74 - onClose(); 69 + try { 70 + await addVolume({ 71 + sandboxId, 72 + name: data.name, 73 + path: data.path, 74 + }); 75 + setIsLoading(false); 76 + reset(); 77 + onClose(); 78 + notyf.open("primary", "Volume added successfully"); 79 + } catch { 80 + setIsLoading(false); 81 + reset(); 82 + onClose(); 83 + notyf.open("error", "Failed to add volume"); 84 + } 75 85 }; 76 86 77 87 if (!isOpen) return null;
+47
apps/web/src/hooks/useNotyf.ts
··· 1 + import { Notyf } from "notyf"; 2 + 3 + export function useNotyf() { 4 + const notyf = new Notyf({ 5 + duration: 3000, 6 + position: { 7 + x: "right", 8 + y: "bottom", 9 + }, 10 + types: [ 11 + { 12 + type: "primary", 13 + background: "var(--color-primary)", 14 + icon: { 15 + className: "icon-[tabler--circle-check] text-white!", 16 + tagName: "i", 17 + }, 18 + }, 19 + { 20 + type: "error", 21 + background: "var(--color-error)", 22 + icon: { 23 + className: "icon-[tabler--circle-x] text-white!", 24 + tagName: "i", 25 + }, 26 + }, 27 + ], 28 + }); 29 + 30 + const open = ( 31 + type: string, 32 + message: string, 33 + duration = 3000, 34 + ripple = false, 35 + dismissible = true, 36 + ) => { 37 + notyf.open({ 38 + type, 39 + message, 40 + duration, 41 + ripple, 42 + dismissible, 43 + }); 44 + }; 45 + 46 + return { open }; 47 + }
+7
apps/web/src/index.css
··· 186 186 .is-invalid { 187 187 border-color: var(--color-error) !important; 188 188 } 189 + 190 + .notyf__icon { 191 + background-color: transparent !important; 192 + box-shadow: none !important; 193 + border: none !important; 194 + color: #fff; 195 + }
+1
apps/web/src/main.tsx
··· 4 4 import { createRouter, RouterProvider } from "@tanstack/react-router"; 5 5 import { routeTree } from "./routeTree.gen"; 6 6 import "flyonui/flyonui"; 7 + import "notyf/notyf.min.css"; 7 8 8 9 const queryClient = new QueryClient(); 9 10 const router = createRouter({ routeTree });