Schedule posts to Bluesky with Cloudflare workers. skyscheduler.work
cf tool bsky-tool cloudflare bluesky schedule bsky service social-media cloudflare-workers

add additional index

Also fix up some of the script commands because they were backwards

+707 -3
+1
migrations/0008_purple_richard_fisk.sql
··· 1 + CREATE INDEX `postedUUID_idx` ON `posts` (`uuid`,`posted`);
+694
migrations/meta/0008_snapshot.json
··· 1 + { 2 + "version": "6", 3 + "dialect": "sqlite", 4 + "id": "1672d539-9622-4971-aa14-c8adec8dbac2", 5 + "prevId": "fd63d60b-8e72-495d-985f-ff7a83b3a0fd", 6 + "tables": { 7 + "accounts": { 8 + "name": "accounts", 9 + "columns": { 10 + "id": { 11 + "name": "id", 12 + "type": "text", 13 + "primaryKey": true, 14 + "notNull": true, 15 + "autoincrement": false 16 + }, 17 + "account_id": { 18 + "name": "account_id", 19 + "type": "text", 20 + "primaryKey": false, 21 + "notNull": true, 22 + "autoincrement": false 23 + }, 24 + "provider_id": { 25 + "name": "provider_id", 26 + "type": "text", 27 + "primaryKey": false, 28 + "notNull": true, 29 + "autoincrement": false 30 + }, 31 + "user_id": { 32 + "name": "user_id", 33 + "type": "text", 34 + "primaryKey": false, 35 + "notNull": true, 36 + "autoincrement": false 37 + }, 38 + "access_token": { 39 + "name": "access_token", 40 + "type": "text", 41 + "primaryKey": false, 42 + "notNull": false, 43 + "autoincrement": false 44 + }, 45 + "refresh_token": { 46 + "name": "refresh_token", 47 + "type": "text", 48 + "primaryKey": false, 49 + "notNull": false, 50 + "autoincrement": false 51 + }, 52 + "id_token": { 53 + "name": "id_token", 54 + "type": "text", 55 + "primaryKey": false, 56 + "notNull": false, 57 + "autoincrement": false 58 + }, 59 + "access_token_expires_at": { 60 + "name": "access_token_expires_at", 61 + "type": "integer", 62 + "primaryKey": false, 63 + "notNull": false, 64 + "autoincrement": false 65 + }, 66 + "refresh_token_expires_at": { 67 + "name": "refresh_token_expires_at", 68 + "type": "integer", 69 + "primaryKey": false, 70 + "notNull": false, 71 + "autoincrement": false 72 + }, 73 + "scope": { 74 + "name": "scope", 75 + "type": "text", 76 + "primaryKey": false, 77 + "notNull": false, 78 + "autoincrement": false 79 + }, 80 + "password": { 81 + "name": "password", 82 + "type": "text", 83 + "primaryKey": false, 84 + "notNull": false, 85 + "autoincrement": false 86 + }, 87 + "created_at": { 88 + "name": "created_at", 89 + "type": "integer", 90 + "primaryKey": false, 91 + "notNull": true, 92 + "autoincrement": false, 93 + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" 94 + }, 95 + "updated_at": { 96 + "name": "updated_at", 97 + "type": "integer", 98 + "primaryKey": false, 99 + "notNull": true, 100 + "autoincrement": false 101 + } 102 + }, 103 + "indexes": { 104 + "accounts_userId_idx": { 105 + "name": "accounts_userId_idx", 106 + "columns": [ 107 + "user_id" 108 + ], 109 + "isUnique": false 110 + } 111 + }, 112 + "foreignKeys": { 113 + "accounts_user_id_users_id_fk": { 114 + "name": "accounts_user_id_users_id_fk", 115 + "tableFrom": "accounts", 116 + "tableTo": "users", 117 + "columnsFrom": [ 118 + "user_id" 119 + ], 120 + "columnsTo": [ 121 + "id" 122 + ], 123 + "onDelete": "cascade", 124 + "onUpdate": "no action" 125 + } 126 + }, 127 + "compositePrimaryKeys": {}, 128 + "uniqueConstraints": {}, 129 + "checkConstraints": {} 130 + }, 131 + "sessions": { 132 + "name": "sessions", 133 + "columns": { 134 + "id": { 135 + "name": "id", 136 + "type": "text", 137 + "primaryKey": true, 138 + "notNull": true, 139 + "autoincrement": false 140 + }, 141 + "expires_at": { 142 + "name": "expires_at", 143 + "type": "integer", 144 + "primaryKey": false, 145 + "notNull": true, 146 + "autoincrement": false 147 + }, 148 + "token": { 149 + "name": "token", 150 + "type": "text", 151 + "primaryKey": false, 152 + "notNull": true, 153 + "autoincrement": false 154 + }, 155 + "created_at": { 156 + "name": "created_at", 157 + "type": "integer", 158 + "primaryKey": false, 159 + "notNull": true, 160 + "autoincrement": false, 161 + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" 162 + }, 163 + "updated_at": { 164 + "name": "updated_at", 165 + "type": "integer", 166 + "primaryKey": false, 167 + "notNull": true, 168 + "autoincrement": false 169 + }, 170 + "ip_address": { 171 + "name": "ip_address", 172 + "type": "text", 173 + "primaryKey": false, 174 + "notNull": false, 175 + "autoincrement": false 176 + }, 177 + "user_agent": { 178 + "name": "user_agent", 179 + "type": "text", 180 + "primaryKey": false, 181 + "notNull": false, 182 + "autoincrement": false 183 + }, 184 + "user_id": { 185 + "name": "user_id", 186 + "type": "text", 187 + "primaryKey": false, 188 + "notNull": true, 189 + "autoincrement": false 190 + } 191 + }, 192 + "indexes": { 193 + "sessions_token_unique": { 194 + "name": "sessions_token_unique", 195 + "columns": [ 196 + "token" 197 + ], 198 + "isUnique": true 199 + }, 200 + "sessions_userId_idx": { 201 + "name": "sessions_userId_idx", 202 + "columns": [ 203 + "user_id" 204 + ], 205 + "isUnique": false 206 + } 207 + }, 208 + "foreignKeys": { 209 + "sessions_user_id_users_id_fk": { 210 + "name": "sessions_user_id_users_id_fk", 211 + "tableFrom": "sessions", 212 + "tableTo": "users", 213 + "columnsFrom": [ 214 + "user_id" 215 + ], 216 + "columnsTo": [ 217 + "id" 218 + ], 219 + "onDelete": "cascade", 220 + "onUpdate": "no action" 221 + } 222 + }, 223 + "compositePrimaryKeys": {}, 224 + "uniqueConstraints": {}, 225 + "checkConstraints": {} 226 + }, 227 + "users": { 228 + "name": "users", 229 + "columns": { 230 + "id": { 231 + "name": "id", 232 + "type": "text", 233 + "primaryKey": true, 234 + "notNull": true, 235 + "autoincrement": false 236 + }, 237 + "name": { 238 + "name": "name", 239 + "type": "text", 240 + "primaryKey": false, 241 + "notNull": true, 242 + "autoincrement": false 243 + }, 244 + "email": { 245 + "name": "email", 246 + "type": "text", 247 + "primaryKey": false, 248 + "notNull": true, 249 + "autoincrement": false 250 + }, 251 + "email_verified": { 252 + "name": "email_verified", 253 + "type": "integer", 254 + "primaryKey": false, 255 + "notNull": true, 256 + "autoincrement": false 257 + }, 258 + "image": { 259 + "name": "image", 260 + "type": "text", 261 + "primaryKey": false, 262 + "notNull": false, 263 + "autoincrement": false 264 + }, 265 + "created_at": { 266 + "name": "created_at", 267 + "type": "integer", 268 + "primaryKey": false, 269 + "notNull": true, 270 + "autoincrement": false 271 + }, 272 + "updated_at": { 273 + "name": "updated_at", 274 + "type": "integer", 275 + "primaryKey": false, 276 + "notNull": true, 277 + "autoincrement": false 278 + }, 279 + "username": { 280 + "name": "username", 281 + "type": "text", 282 + "primaryKey": false, 283 + "notNull": false, 284 + "autoincrement": false 285 + }, 286 + "display_username": { 287 + "name": "display_username", 288 + "type": "text", 289 + "primaryKey": false, 290 + "notNull": false, 291 + "autoincrement": false 292 + }, 293 + "bsky_app_pass": { 294 + "name": "bsky_app_pass", 295 + "type": "text", 296 + "primaryKey": false, 297 + "notNull": true, 298 + "autoincrement": false 299 + }, 300 + "pds": { 301 + "name": "pds", 302 + "type": "text", 303 + "primaryKey": false, 304 + "notNull": true, 305 + "autoincrement": false, 306 + "default": "'https://bsky.social'" 307 + } 308 + }, 309 + "indexes": { 310 + "users_email_unique": { 311 + "name": "users_email_unique", 312 + "columns": [ 313 + "email" 314 + ], 315 + "isUnique": true 316 + }, 317 + "users_username_unique": { 318 + "name": "users_username_unique", 319 + "columns": [ 320 + "username" 321 + ], 322 + "isUnique": true 323 + } 324 + }, 325 + "foreignKeys": {}, 326 + "compositePrimaryKeys": {}, 327 + "uniqueConstraints": {}, 328 + "checkConstraints": {} 329 + }, 330 + "verifications": { 331 + "name": "verifications", 332 + "columns": { 333 + "id": { 334 + "name": "id", 335 + "type": "text", 336 + "primaryKey": true, 337 + "notNull": true, 338 + "autoincrement": false 339 + }, 340 + "identifier": { 341 + "name": "identifier", 342 + "type": "text", 343 + "primaryKey": false, 344 + "notNull": true, 345 + "autoincrement": false 346 + }, 347 + "value": { 348 + "name": "value", 349 + "type": "text", 350 + "primaryKey": false, 351 + "notNull": true, 352 + "autoincrement": false 353 + }, 354 + "expires_at": { 355 + "name": "expires_at", 356 + "type": "integer", 357 + "primaryKey": false, 358 + "notNull": true, 359 + "autoincrement": false 360 + }, 361 + "created_at": { 362 + "name": "created_at", 363 + "type": "integer", 364 + "primaryKey": false, 365 + "notNull": true, 366 + "autoincrement": false, 367 + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" 368 + }, 369 + "updated_at": { 370 + "name": "updated_at", 371 + "type": "integer", 372 + "primaryKey": false, 373 + "notNull": true, 374 + "autoincrement": false, 375 + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" 376 + } 377 + }, 378 + "indexes": { 379 + "verifications_identifier_idx": { 380 + "name": "verifications_identifier_idx", 381 + "columns": [ 382 + "identifier" 383 + ], 384 + "isUnique": false 385 + } 386 + }, 387 + "foreignKeys": {}, 388 + "compositePrimaryKeys": {}, 389 + "uniqueConstraints": {}, 390 + "checkConstraints": {} 391 + }, 392 + "posts": { 393 + "name": "posts", 394 + "columns": { 395 + "uuid": { 396 + "name": "uuid", 397 + "type": "text", 398 + "primaryKey": true, 399 + "notNull": true, 400 + "autoincrement": false 401 + }, 402 + "content": { 403 + "name": "content", 404 + "type": "text", 405 + "primaryKey": false, 406 + "notNull": true, 407 + "autoincrement": false 408 + }, 409 + "scheduled_date": { 410 + "name": "scheduled_date", 411 + "type": "integer", 412 + "primaryKey": false, 413 + "notNull": true, 414 + "autoincrement": false 415 + }, 416 + "posted": { 417 + "name": "posted", 418 + "type": "integer", 419 + "primaryKey": false, 420 + "notNull": false, 421 + "autoincrement": false, 422 + "default": false 423 + }, 424 + "postNow": { 425 + "name": "postNow", 426 + "type": "integer", 427 + "primaryKey": false, 428 + "notNull": false, 429 + "autoincrement": false, 430 + "default": false 431 + }, 432 + "embedContent": { 433 + "name": "embedContent", 434 + "type": "text", 435 + "primaryKey": false, 436 + "notNull": true, 437 + "autoincrement": false, 438 + "default": "(json_array())" 439 + }, 440 + "uri": { 441 + "name": "uri", 442 + "type": "text", 443 + "primaryKey": false, 444 + "notNull": false, 445 + "autoincrement": false 446 + }, 447 + "cid": { 448 + "name": "cid", 449 + "type": "text", 450 + "primaryKey": false, 451 + "notNull": false, 452 + "autoincrement": false 453 + }, 454 + "contentLabel": { 455 + "name": "contentLabel", 456 + "type": "text", 457 + "primaryKey": false, 458 + "notNull": true, 459 + "autoincrement": false, 460 + "default": "'None'" 461 + }, 462 + "created_at": { 463 + "name": "created_at", 464 + "type": "integer", 465 + "primaryKey": false, 466 + "notNull": true, 467 + "autoincrement": false, 468 + "default": "CURRENT_TIMESTAMP" 469 + }, 470 + "updated_at": { 471 + "name": "updated_at", 472 + "type": "integer", 473 + "primaryKey": false, 474 + "notNull": false, 475 + "autoincrement": false 476 + }, 477 + "user": { 478 + "name": "user", 479 + "type": "text", 480 + "primaryKey": false, 481 + "notNull": true, 482 + "autoincrement": false 483 + } 484 + }, 485 + "indexes": { 486 + "scheduledDate_idx": { 487 + "name": "scheduledDate_idx", 488 + "columns": [ 489 + "scheduled_date" 490 + ], 491 + "isUnique": false 492 + }, 493 + "user_idx": { 494 + "name": "user_idx", 495 + "columns": [ 496 + "user" 497 + ], 498 + "isUnique": false 499 + }, 500 + "postedUpdate_idx": { 501 + "name": "postedUpdate_idx", 502 + "columns": [ 503 + "updated_at", 504 + "posted" 505 + ], 506 + "isUnique": false 507 + }, 508 + "postedUUID_idx": { 509 + "name": "postedUUID_idx", 510 + "columns": [ 511 + "uuid", 512 + "posted" 513 + ], 514 + "isUnique": false 515 + } 516 + }, 517 + "foreignKeys": { 518 + "posts_user_users_id_fk": { 519 + "name": "posts_user_users_id_fk", 520 + "tableFrom": "posts", 521 + "tableTo": "users", 522 + "columnsFrom": [ 523 + "user" 524 + ], 525 + "columnsTo": [ 526 + "id" 527 + ], 528 + "onDelete": "cascade", 529 + "onUpdate": "no action" 530 + } 531 + }, 532 + "compositePrimaryKeys": {}, 533 + "uniqueConstraints": {}, 534 + "checkConstraints": {} 535 + }, 536 + "reposts": { 537 + "name": "reposts", 538 + "columns": { 539 + "id": { 540 + "name": "id", 541 + "type": "integer", 542 + "primaryKey": true, 543 + "notNull": true, 544 + "autoincrement": true 545 + }, 546 + "post_uuid": { 547 + "name": "post_uuid", 548 + "type": "text", 549 + "primaryKey": false, 550 + "notNull": true, 551 + "autoincrement": false 552 + }, 553 + "scheduled_date": { 554 + "name": "scheduled_date", 555 + "type": "integer", 556 + "primaryKey": false, 557 + "notNull": true, 558 + "autoincrement": false 559 + } 560 + }, 561 + "indexes": { 562 + "repost_scheduledDate_idx": { 563 + "name": "repost_scheduledDate_idx", 564 + "columns": [ 565 + "scheduled_date" 566 + ], 567 + "isUnique": false 568 + }, 569 + "repost_postid_idx": { 570 + "name": "repost_postid_idx", 571 + "columns": [ 572 + "post_uuid" 573 + ], 574 + "isUnique": false 575 + } 576 + }, 577 + "foreignKeys": { 578 + "reposts_post_uuid_posts_uuid_fk": { 579 + "name": "reposts_post_uuid_posts_uuid_fk", 580 + "tableFrom": "reposts", 581 + "tableTo": "posts", 582 + "columnsFrom": [ 583 + "post_uuid" 584 + ], 585 + "columnsTo": [ 586 + "uuid" 587 + ], 588 + "onDelete": "cascade", 589 + "onUpdate": "no action" 590 + } 591 + }, 592 + "compositePrimaryKeys": {}, 593 + "uniqueConstraints": {}, 594 + "checkConstraints": {} 595 + }, 596 + "violations": { 597 + "name": "violations", 598 + "columns": { 599 + "user": { 600 + "name": "user", 601 + "type": "text", 602 + "primaryKey": true, 603 + "notNull": true, 604 + "autoincrement": false 605 + }, 606 + "tosViolation": { 607 + "name": "tosViolation", 608 + "type": "integer", 609 + "primaryKey": false, 610 + "notNull": false, 611 + "autoincrement": false, 612 + "default": false 613 + }, 614 + "userPassInvalid": { 615 + "name": "userPassInvalid", 616 + "type": "integer", 617 + "primaryKey": false, 618 + "notNull": false, 619 + "autoincrement": false, 620 + "default": false 621 + }, 622 + "accountSuspended": { 623 + "name": "accountSuspended", 624 + "type": "integer", 625 + "primaryKey": false, 626 + "notNull": false, 627 + "autoincrement": false, 628 + "default": false 629 + }, 630 + "accountGone": { 631 + "name": "accountGone", 632 + "type": "integer", 633 + "primaryKey": false, 634 + "notNull": false, 635 + "autoincrement": false, 636 + "default": false 637 + }, 638 + "mediaTooBig": { 639 + "name": "mediaTooBig", 640 + "type": "integer", 641 + "primaryKey": false, 642 + "notNull": false, 643 + "autoincrement": false, 644 + "default": false 645 + }, 646 + "created_at": { 647 + "name": "created_at", 648 + "type": "integer", 649 + "primaryKey": false, 650 + "notNull": true, 651 + "autoincrement": false, 652 + "default": "CURRENT_TIMESTAMP" 653 + } 654 + }, 655 + "indexes": { 656 + "violations_user_idx": { 657 + "name": "violations_user_idx", 658 + "columns": [ 659 + "user" 660 + ], 661 + "isUnique": false 662 + } 663 + }, 664 + "foreignKeys": { 665 + "violations_user_users_id_fk": { 666 + "name": "violations_user_users_id_fk", 667 + "tableFrom": "violations", 668 + "tableTo": "users", 669 + "columnsFrom": [ 670 + "user" 671 + ], 672 + "columnsTo": [ 673 + "id" 674 + ], 675 + "onDelete": "cascade", 676 + "onUpdate": "no action" 677 + } 678 + }, 679 + "compositePrimaryKeys": {}, 680 + "uniqueConstraints": {}, 681 + "checkConstraints": {} 682 + } 683 + }, 684 + "views": {}, 685 + "enums": {}, 686 + "_meta": { 687 + "schemas": {}, 688 + "tables": {}, 689 + "columns": {} 690 + }, 691 + "internal": { 692 + "indexes": {} 693 + } 694 + }
+7
migrations/meta/_journal.json
··· 57 57 "when": 1767557196194, 58 58 "tag": "0007_pink_scarlet_witch", 59 59 "breakpoints": true 60 + }, 61 + { 62 + "idx": 8, 63 + "version": "6", 64 + "when": 1767566050928, 65 + "tag": "0008_purple_richard_fisk", 66 + "breakpoints": true 60 67 } 61 68 ] 62 69 }
+3 -2
package.json
··· 10 10 "migrate:local:db": "wrangler d1 migrations apply skyposts --local", 11 11 "migrate:local:pragma": "wrangler d1 execute skyposts --command \"PRAGMA optimize\" --local -y", 12 12 "migrate:local": "run-s migrate:local:*", 13 - "migrate:prod:db": "wrangler d1 execute skyposts --command \"PRAGMA optimize\" --remote -y", 14 - "migrate:prod:pragma": "wrangler d1 migrations apply skyposts --remote", 13 + "migrate:prod:db": "wrangler d1 migrations apply skyposts --remote", 14 + "migrate:prod:pragma": "wrangler d1 execute skyposts --command \"PRAGMA optimize\" --remote -y", 15 15 "migrate:prod": "run-s migrate:prod:*", 16 16 "migrate:remote": "npm run migrate:prod", 17 + "migrate:optimize": "npm run migrate:prod:pragma && npm run migrate:local:pragma", 17 18 "migrate:all": "npm run migrate:local && npm run migrate:prod", 18 19 "minify:post": "minify assets/js/postHelper.js > assets/js/postHelper.min.js", 19 20 "minify:main": "minify assets/js/main.js > assets/js/main.min.js",
+2 -1
src/db/app.schema.ts
··· 25 25 }, (table) => [ 26 26 index("scheduledDate_idx").on(table.scheduledDate), 27 27 index("user_idx").on(table.userId), 28 - index("postedUpdate_idx").on(table.updatedAt, table.posted) 28 + index("postedUpdate_idx").on(table.updatedAt, table.posted), 29 + index("postedUUID_idx").on(table.uuid, table.posted) 29 30 ]); 30 31 31 32 export const reposts = sqliteTable('reposts', {