social bookmarking for atproto

[appview] implement basic database schema, update some other things

hexmani.ac d5145b05 089932e9

verified
+1 -1
.idea/clippr.iml
··· 8 8 <excludeFolder url="file://$MODULE_DIR$/backend/.dart_tool" /> 9 9 <excludeFolder url="file://$MODULE_DIR$/backend/.pub" /> 10 10 <excludeFolder url="file://$MODULE_DIR$/backend/build" /> 11 + <excludeFolder url="file://$MODULE_DIR$/.idea/dataSources" /> 11 12 </content> 12 13 <orderEntry type="inheritedJdk" /> 13 14 <orderEntry type="sourceFolder" forTests="false" /> 14 15 <orderEntry type="library" name="Dart SDK" level="project" /> 15 - <orderEntry type="library" name="Dart Packages" level="project" /> 16 16 </component> 17 17 </module>
+1 -1
README.md
··· 5 5 the project is sorted into two main directories, comprised of: 6 6 7 7 * ``backend`` - the reference appview written using node and hono 8 - * ``frontend`` - the referenwce frontend written in astro and typescript 8 + * ``frontend`` - the reference frontend written in astro and typescript
+2 -1
backend/.gitignore
··· 1 1 # deps 2 2 ../../clippr-ts/node_modules/ 3 - config.toml 3 + config.toml 4 + *.db
+3 -1
backend/config.example.toml
··· 6 6 port = 9090 7 7 8 8 ## How the SQLite database is stored. 9 + ## For testing, you can store the database in memory with ":memory:" 9 10 [database] 10 - name = "clippr.db" 11 + ## Paths can be used here. 12 + name = "file:clippr.db" 11 13 12 14 ## How the server interacts with the ATproto network. 13 15 [network]
+7 -2
backend/drizzle.config.ts
··· 1 - import 'dotenv/config'; 1 + /* 2 + * clippr: a social bookmarking service for the AT Protocol 3 + * Copyright (c) 2025 clippr contributors. 4 + * SPDX-License-Identifier: AGPL-3.0-only 5 + */ 6 + 2 7 import {defineConfig} from 'drizzle-kit'; 3 - import { Config } from "./src/config"; 8 + import {Config} from "./src/config.ts"; 4 9 5 10 const config = Config.getInstance(); 6 11 const dbname = config.get("database.name");
+11 -6
backend/package.json
··· 3 3 "version": "0.1.0", 4 4 "private": "true", 5 5 "homepage": "https://tangled.sh/@hexmani.ac/clippr", 6 - "license": "AGPL-3.0-or-later", 6 + "license": "AGPL-3.0-only", 7 7 "scripts": { 8 - "dev": "node --watch src/main.ts" 8 + "dev": "tsx watch src/main.ts", 9 + "build": "tsc", 10 + "start": "node dist/index.js" 9 11 }, 10 12 "type": "module", 11 13 "dependencies": { 12 14 "@hono/node-server": "^1.15.0", 15 + "hono": "^4.8.3", 16 + "drizzle-orm": "^0.44.2", 17 + "@libsql/client": "^0.15.9", 13 18 "@skyware/jetstream": "^0.2.2", 14 - "drizzle-orm": "^0.44.2", 15 - "hono": "^4.8.3", 16 19 "toml": "^3.0.0" 17 20 }, 18 21 "devDependencies": { 22 + "eslint": "^9.30.1", 19 23 "@eslint/js": "^9.30.1", 24 + "typescript-eslint": "^8.35.1", 20 25 "drizzle-kit": "^0.31.4", 21 - "eslint": "^9.30.1", 22 26 "globals": "^16.3.0", 23 - "typescript-eslint": "^8.35.1" 27 + "tsx": "^4.20.3", 28 + "typescript": "^5.8.3" 24 29 } 25 30 }
+261 -5
backend/pnpm-lock.yaml
··· 11 11 '@hono/node-server': 12 12 specifier: ^1.15.0 13 13 version: 1.15.0(hono@4.8.3) 14 + '@libsql/client': 15 + specifier: ^0.15.9 16 + version: 0.15.9 14 17 '@skyware/jetstream': 15 18 specifier: ^0.2.2 16 19 version: 0.2.2(@atcute/client@2.0.9) 17 20 drizzle-orm: 18 21 specifier: ^0.44.2 19 - version: 0.44.2(bun-types@1.2.17) 22 + version: 0.44.2(@libsql/client@0.15.9)(bun-types@1.2.17) 20 23 hono: 21 24 specifier: ^4.8.3 22 25 version: 4.8.3 ··· 36 39 globals: 37 40 specifier: ^16.3.0 38 41 version: 16.3.0 42 + tsx: 43 + specifier: ^4.20.3 44 + version: 4.20.3 39 45 typescript-eslint: 40 46 specifier: ^8.35.1 41 47 version: 8.35.1(eslint@9.30.1)(typescript@5.8.3) ··· 411 417 resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} 412 418 engines: {node: '>=18.18'} 413 419 420 + '@libsql/client@0.15.9': 421 + resolution: {integrity: sha512-VT3do0a0vwYVaNcp/y05ikkKS3OrFR5UeEf5SUuYZVgKVl1Nc1k9ajoYSsOid8AD/vlhLDB5yFQaV4HmT/OB9w==} 422 + 423 + '@libsql/core@0.15.9': 424 + resolution: {integrity: sha512-4OVdeAmuaCUq5hYT8NNn0nxlO9AcA/eTjXfUZ+QK8MT3Dz7Z76m73x7KxjU6I64WyXX98dauVH2b9XM+d84npw==} 425 + 426 + '@libsql/darwin-arm64@0.5.13': 427 + resolution: {integrity: sha512-ASz/EAMLDLx3oq9PVvZ4zBXXHbz2TxtxUwX2xpTRFR4V4uSHAN07+jpLu3aK5HUBLuv58z7+GjaL5w/cyjR28Q==} 428 + cpu: [arm64] 429 + os: [darwin] 430 + 431 + '@libsql/darwin-x64@0.5.13': 432 + resolution: {integrity: sha512-kzglniv1difkq8opusSXM7u9H0WoEPeKxw0ixIfcGfvlCVMJ+t9UNtXmyNHW68ljdllje6a4C6c94iPmIYafYA==} 433 + cpu: [x64] 434 + os: [darwin] 435 + 436 + '@libsql/hrana-client@0.7.0': 437 + resolution: {integrity: sha512-OF8fFQSkbL7vJY9rfuegK1R7sPgQ6kFMkDamiEccNUvieQ+3urzfDFI616oPl8V7T9zRmnTkSjMOImYCAVRVuw==} 438 + 439 + '@libsql/isomorphic-fetch@0.3.1': 440 + resolution: {integrity: sha512-6kK3SUK5Uu56zPq/Las620n5aS9xJq+jMBcNSOmjhNf/MUvdyji4vrMTqD7ptY7/4/CAVEAYDeotUz60LNQHtw==} 441 + engines: {node: '>=18.0.0'} 442 + 443 + '@libsql/isomorphic-ws@0.1.5': 444 + resolution: {integrity: sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==} 445 + 446 + '@libsql/linux-arm-gnueabihf@0.5.13': 447 + resolution: {integrity: sha512-UEW+VZN2r0mFkfztKOS7cqfS8IemuekbjUXbXCwULHtusww2QNCXvM5KU9eJCNE419SZCb0qaEWYytcfka8qeA==} 448 + cpu: [arm] 449 + os: [linux] 450 + 451 + '@libsql/linux-arm-musleabihf@0.5.13': 452 + resolution: {integrity: sha512-NMDgLqryYBv4Sr3WoO/m++XDjR5KLlw9r/JK4Ym6A1XBv2bxQQNhH0Lxx3bjLW8qqhBD4+0xfms4d2cOlexPyA==} 453 + cpu: [arm] 454 + os: [linux] 455 + 456 + '@libsql/linux-arm64-gnu@0.5.13': 457 + resolution: {integrity: sha512-/wCxVdrwl1ee6D6LEjwl+w4SxuLm5UL9Kb1LD5n0bBGs0q+49ChdPPh7tp175iRgkcrTgl23emymvt1yj3KxVQ==} 458 + cpu: [arm64] 459 + os: [linux] 460 + 461 + '@libsql/linux-arm64-musl@0.5.13': 462 + resolution: {integrity: sha512-xnVAbZIanUgX57XqeI5sNaDnVilp0Di5syCLSEo+bRyBobe/1IAeehNZpyVbCy91U2N6rH1C/mZU7jicVI9x+A==} 463 + cpu: [arm64] 464 + os: [linux] 465 + 466 + '@libsql/linux-x64-gnu@0.5.13': 467 + resolution: {integrity: sha512-/mfMRxcQAI9f8t7tU3QZyh25lXgXKzgin9B9TOSnchD73PWtsVhlyfA6qOCfjQl5kr4sHscdXD5Yb3KIoUgrpQ==} 468 + cpu: [x64] 469 + os: [linux] 470 + 471 + '@libsql/linux-x64-musl@0.5.13': 472 + resolution: {integrity: sha512-rdefPTpQCVwUjIQYbDLMv3qpd5MdrT0IeD0UZPGqhT9AWU8nJSQoj2lfyIDAWEz7PPOVCY4jHuEn7FS2sw9kRA==} 473 + cpu: [x64] 474 + os: [linux] 475 + 476 + '@libsql/win32-x64-msvc@0.5.13': 477 + resolution: {integrity: sha512-aNcmDrD1Ws+dNZIv9ECbxBQumqB9MlSVEykwfXJpqv/593nABb8Ttg5nAGUPtnADyaGDTrGvPPP81d/KsKho4Q==} 478 + cpu: [x64] 479 + os: [win32] 480 + 481 + '@neon-rs/load@0.0.4': 482 + resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} 483 + 414 484 '@nodelib/fs.scandir@2.1.5': 415 485 resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 416 486 engines: {node: '>= 8'} ··· 434 504 435 505 '@types/node@24.0.10': 436 506 resolution: {integrity: sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==} 507 + 508 + '@types/ws@8.18.1': 509 + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} 437 510 438 511 '@typescript-eslint/eslint-plugin@8.35.1': 439 512 resolution: {integrity: sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==} ··· 555 628 resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 556 629 engines: {node: '>= 8'} 557 630 631 + data-uri-to-buffer@4.0.1: 632 + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} 633 + engines: {node: '>= 12'} 634 + 558 635 debug@4.4.1: 559 636 resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} 560 637 engines: {node: '>=6.0'} ··· 566 643 567 644 deep-is@0.1.4: 568 645 resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 646 + 647 + detect-libc@2.0.2: 648 + resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} 649 + engines: {node: '>=8'} 569 650 570 651 drizzle-kit@0.31.4: 571 652 resolution: {integrity: sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==} ··· 743 824 fastq@1.19.1: 744 825 resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} 745 826 827 + fetch-blob@3.2.0: 828 + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} 829 + engines: {node: ^12.20 || >= 14.13} 830 + 746 831 file-entry-cache@8.0.0: 747 832 resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 748 833 engines: {node: '>=16.0.0'} ··· 761 846 762 847 flatted@3.3.3: 763 848 resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} 849 + 850 + formdata-polyfill@4.0.10: 851 + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} 852 + engines: {node: '>=12.20.0'} 853 + 854 + fsevents@2.3.3: 855 + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 856 + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 857 + os: [darwin] 764 858 765 859 get-tsconfig@4.10.1: 766 860 resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} ··· 823 917 isexe@2.0.0: 824 918 resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 825 919 920 + js-base64@3.7.7: 921 + resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} 922 + 826 923 js-yaml@4.1.0: 827 924 resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 828 925 hasBin: true ··· 843 940 resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 844 941 engines: {node: '>= 0.8.0'} 845 942 943 + libsql@0.5.13: 944 + resolution: {integrity: sha512-5Bwoa/CqzgkTwySgqHA5TsaUDRrdLIbdM4egdPcaAnqO3aC+qAgS6BwdzuZwARA5digXwiskogZ8H7Yy4XfdOg==} 945 + cpu: [x64, arm64, wasm32, arm] 946 + os: [darwin, linux, win32] 947 + 846 948 locate-path@6.0.0: 847 949 resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 848 950 engines: {node: '>=10'} ··· 871 973 natural-compare@1.4.0: 872 974 resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 873 975 976 + node-domexception@1.0.0: 977 + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} 978 + engines: {node: '>=10.5.0'} 979 + deprecated: Use your platform's native DOMException instead 980 + 981 + node-fetch@3.3.2: 982 + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} 983 + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 984 + 874 985 optionator@0.9.4: 875 986 resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 876 987 engines: {node: '>= 0.8.0'} ··· 905 1016 prelude-ls@1.2.1: 906 1017 resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 907 1018 engines: {node: '>= 0.8.0'} 1019 + 1020 + promise-limit@2.7.0: 1021 + resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==} 908 1022 909 1023 punycode@2.3.1: 910 1024 resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} ··· 968 1082 peerDependencies: 969 1083 typescript: '>=4.8.4' 970 1084 1085 + tsx@4.20.3: 1086 + resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} 1087 + engines: {node: '>=18.0.0'} 1088 + hasBin: true 1089 + 971 1090 type-check@0.4.0: 972 1091 resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 973 1092 engines: {node: '>= 0.8.0'} ··· 989 1108 990 1109 uri-js@4.4.1: 991 1110 resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 1111 + 1112 + web-streams-polyfill@3.3.3: 1113 + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} 1114 + engines: {node: '>= 8'} 992 1115 993 1116 which@2.0.2: 994 1117 resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} ··· 999 1122 resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 1000 1123 engines: {node: '>=0.10.0'} 1001 1124 1125 + ws@8.18.3: 1126 + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} 1127 + engines: {node: '>=10.0.0'} 1128 + peerDependencies: 1129 + bufferutil: ^4.0.1 1130 + utf-8-validate: '>=5.0.2' 1131 + peerDependenciesMeta: 1132 + bufferutil: 1133 + optional: true 1134 + utf-8-validate: 1135 + optional: true 1136 + 1002 1137 yocto-queue@0.1.0: 1003 1138 resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 1004 1139 engines: {node: '>=10'} ··· 1229 1364 1230 1365 '@humanwhocodes/retry@0.4.3': {} 1231 1366 1367 + '@libsql/client@0.15.9': 1368 + dependencies: 1369 + '@libsql/core': 0.15.9 1370 + '@libsql/hrana-client': 0.7.0 1371 + js-base64: 3.7.7 1372 + libsql: 0.5.13 1373 + promise-limit: 2.7.0 1374 + transitivePeerDependencies: 1375 + - bufferutil 1376 + - utf-8-validate 1377 + 1378 + '@libsql/core@0.15.9': 1379 + dependencies: 1380 + js-base64: 3.7.7 1381 + 1382 + '@libsql/darwin-arm64@0.5.13': 1383 + optional: true 1384 + 1385 + '@libsql/darwin-x64@0.5.13': 1386 + optional: true 1387 + 1388 + '@libsql/hrana-client@0.7.0': 1389 + dependencies: 1390 + '@libsql/isomorphic-fetch': 0.3.1 1391 + '@libsql/isomorphic-ws': 0.1.5 1392 + js-base64: 3.7.7 1393 + node-fetch: 3.3.2 1394 + transitivePeerDependencies: 1395 + - bufferutil 1396 + - utf-8-validate 1397 + 1398 + '@libsql/isomorphic-fetch@0.3.1': {} 1399 + 1400 + '@libsql/isomorphic-ws@0.1.5': 1401 + dependencies: 1402 + '@types/ws': 8.18.1 1403 + ws: 8.18.3 1404 + transitivePeerDependencies: 1405 + - bufferutil 1406 + - utf-8-validate 1407 + 1408 + '@libsql/linux-arm-gnueabihf@0.5.13': 1409 + optional: true 1410 + 1411 + '@libsql/linux-arm-musleabihf@0.5.13': 1412 + optional: true 1413 + 1414 + '@libsql/linux-arm64-gnu@0.5.13': 1415 + optional: true 1416 + 1417 + '@libsql/linux-arm64-musl@0.5.13': 1418 + optional: true 1419 + 1420 + '@libsql/linux-x64-gnu@0.5.13': 1421 + optional: true 1422 + 1423 + '@libsql/linux-x64-musl@0.5.13': 1424 + optional: true 1425 + 1426 + '@libsql/win32-x64-msvc@0.5.13': 1427 + optional: true 1428 + 1429 + '@neon-rs/load@0.0.4': {} 1430 + 1232 1431 '@nodelib/fs.scandir@2.1.5': 1233 1432 dependencies: 1234 1433 '@nodelib/fs.stat': 2.0.5 ··· 1255 1454 '@types/node@24.0.10': 1256 1455 dependencies: 1257 1456 undici-types: 7.8.0 1258 - optional: true 1457 + 1458 + '@types/ws@8.18.1': 1459 + dependencies: 1460 + '@types/node': 24.0.10 1259 1461 1260 1462 '@typescript-eslint/eslint-plugin@8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1)(typescript@5.8.3)': 1261 1463 dependencies: ··· 1411 1613 shebang-command: 2.0.0 1412 1614 which: 2.0.2 1413 1615 1616 + data-uri-to-buffer@4.0.1: {} 1617 + 1414 1618 debug@4.4.1: 1415 1619 dependencies: 1416 1620 ms: 2.1.3 1417 1621 1418 1622 deep-is@0.1.4: {} 1419 1623 1624 + detect-libc@2.0.2: {} 1625 + 1420 1626 drizzle-kit@0.31.4: 1421 1627 dependencies: 1422 1628 '@drizzle-team/brocli': 0.10.2 ··· 1426 1632 transitivePeerDependencies: 1427 1633 - supports-color 1428 1634 1429 - drizzle-orm@0.44.2(bun-types@1.2.17): 1635 + drizzle-orm@0.44.2(@libsql/client@0.15.9)(bun-types@1.2.17): 1430 1636 optionalDependencies: 1637 + '@libsql/client': 0.15.9 1431 1638 bun-types: 1.2.17 1432 1639 1433 1640 esbuild-register@3.6.0(esbuild@0.25.5): ··· 1579 1786 dependencies: 1580 1787 reusify: 1.1.0 1581 1788 1789 + fetch-blob@3.2.0: 1790 + dependencies: 1791 + node-domexception: 1.0.0 1792 + web-streams-polyfill: 3.3.3 1793 + 1582 1794 file-entry-cache@8.0.0: 1583 1795 dependencies: 1584 1796 flat-cache: 4.0.1 ··· 1598 1810 keyv: 4.5.4 1599 1811 1600 1812 flatted@3.3.3: {} 1813 + 1814 + formdata-polyfill@4.0.10: 1815 + dependencies: 1816 + fetch-blob: 3.2.0 1817 + 1818 + fsevents@2.3.3: 1819 + optional: true 1601 1820 1602 1821 get-tsconfig@4.10.1: 1603 1822 dependencies: ··· 1642 1861 1643 1862 isexe@2.0.0: {} 1644 1863 1864 + js-base64@3.7.7: {} 1865 + 1645 1866 js-yaml@4.1.0: 1646 1867 dependencies: 1647 1868 argparse: 2.0.1 ··· 1661 1882 prelude-ls: 1.2.1 1662 1883 type-check: 0.4.0 1663 1884 1885 + libsql@0.5.13: 1886 + dependencies: 1887 + '@neon-rs/load': 0.0.4 1888 + detect-libc: 2.0.2 1889 + optionalDependencies: 1890 + '@libsql/darwin-arm64': 0.5.13 1891 + '@libsql/darwin-x64': 0.5.13 1892 + '@libsql/linux-arm-gnueabihf': 0.5.13 1893 + '@libsql/linux-arm-musleabihf': 0.5.13 1894 + '@libsql/linux-arm64-gnu': 0.5.13 1895 + '@libsql/linux-arm64-musl': 0.5.13 1896 + '@libsql/linux-x64-gnu': 0.5.13 1897 + '@libsql/linux-x64-musl': 0.5.13 1898 + '@libsql/win32-x64-msvc': 0.5.13 1899 + 1664 1900 locate-path@6.0.0: 1665 1901 dependencies: 1666 1902 p-locate: 5.0.0 ··· 1685 1921 ms@2.1.3: {} 1686 1922 1687 1923 natural-compare@1.4.0: {} 1924 + 1925 + node-domexception@1.0.0: {} 1926 + 1927 + node-fetch@3.3.2: 1928 + dependencies: 1929 + data-uri-to-buffer: 4.0.1 1930 + fetch-blob: 3.2.0 1931 + formdata-polyfill: 4.0.10 1688 1932 1689 1933 optionator@0.9.4: 1690 1934 dependencies: ··· 1719 1963 1720 1964 prelude-ls@1.2.1: {} 1721 1965 1966 + promise-limit@2.7.0: {} 1967 + 1722 1968 punycode@2.3.1: {} 1723 1969 1724 1970 queue-microtask@1.2.3: {} ··· 1764 2010 dependencies: 1765 2011 typescript: 5.8.3 1766 2012 2013 + tsx@4.20.3: 2014 + dependencies: 2015 + esbuild: 0.25.5 2016 + get-tsconfig: 4.10.1 2017 + optionalDependencies: 2018 + fsevents: 2.3.3 2019 + 1767 2020 type-check@0.4.0: 1768 2021 dependencies: 1769 2022 prelude-ls: 1.2.1 ··· 1780 2033 1781 2034 typescript@5.8.3: {} 1782 2035 1783 - undici-types@7.8.0: 1784 - optional: true 2036 + undici-types@7.8.0: {} 1785 2037 1786 2038 uri-js@4.4.1: 1787 2039 dependencies: 1788 2040 punycode: 2.3.1 1789 2041 2042 + web-streams-polyfill@3.3.3: {} 2043 + 1790 2044 which@2.0.2: 1791 2045 dependencies: 1792 2046 isexe: 2.0.0 1793 2047 1794 2048 word-wrap@1.2.5: {} 2049 + 2050 + ws@8.18.3: {} 1795 2051 1796 2052 yocto-queue@0.1.0: {}
+26 -3
backend/src/db/database.ts
··· 4 4 * SPDX-License-Identifier: AGPL-3.0-only 5 5 */ 6 6 7 - import {drizzle} from 'drizzle-orm/bun-sqlite'; 7 + import {drizzle} from 'drizzle-orm/libsql'; 8 8 import {Config} from "../config.ts"; 9 9 10 10 const config = Config.getInstance(); 11 + const dbname = config.get("database.name"); 11 12 12 - const db = drizzle(config.get("database.name")); 13 - console.log(db); 13 + export class Database { 14 + private static instance: Database; 15 + private readonly db: any; 16 + 17 + private constructor() { 18 + this.db = drizzle({connection: {url: `${dbname}`}}); 19 + } 20 + 21 + static getInstance(): Database { 22 + if (!Database.instance) { 23 + try { 24 + Database.instance = new Database(); 25 + } catch (e) { 26 + console.error(e); 27 + process.exit(1); 28 + } 29 + } 30 + return Database.instance; 31 + } 32 + 33 + getDb() { 34 + return this.db; 35 + } 36 + }
+32 -6
backend/src/db/schema.ts
··· 1 - import { int, sqliteTable, text } from "drizzle-orm/sqlite-core"; 1 + /* 2 + * clippr: a social bookmarking service for the AT Protocol 3 + * Copyright (c) 2025 clippr contributors. 4 + * SPDX-License-Identifier: AGPL-3.0-only 5 + */ 2 6 3 - export const usersTable = sqliteTable("users_table", { 4 - id: int().primaryKey({ autoIncrement: true }), 5 - name: text().notNull(), 6 - age: int().notNull(), 7 - email: text().notNull().unique(), 7 + import {int, sqliteTable, text} from "drizzle-orm/sqlite-core"; 8 + import {sql} from "drizzle-orm"; 9 + 10 + export const clipsTable = sqliteTable("clips", { 11 + id: int('id').primaryKey({autoIncrement: true}), 12 + timestamp: int('time_us', {mode: 'timestamp_ms'}).notNull().default(sql`(unixepoch() * 1000)`), 13 + did: text('did').notNull(), 14 + recordKey: text('rkey').notNull(), 15 + url: text('url').notNull(), 16 + title: text('title').notNull(), 17 + description: text('description').notNull(), 18 + unlisted: int('unlisted', {mode: 'boolean'}).notNull(), 19 + notes: text('notes'), 20 + tags: text('tags', {mode: "json"}).$type<string[]>().default(sql`'[]'`), 21 + unread: int('unread', {mode: 'boolean'}), 22 + languages: text('languages', {mode: 'json'}).$type<string[]>().default(sql`'[]'`), 23 + createdAt: int('createdAt', {mode: 'timestamp_ms'}).notNull().default(sql`(unixepoch() * 1000)`), 24 + }); 25 + 26 + export const tagsTable = sqliteTable("tags", { 27 + id: int('id').primaryKey({autoIncrement: true}), 28 + timestamp: int('time_us', {mode: 'timestamp_ms'}).notNull().default(sql`(unixepoch() * 1000)`), 29 + did: text('did').notNull(), 30 + recordKey: text('rkey').notNull(), 31 + name: text('name').notNull(), 32 + color: text('color'), 33 + createdAt: int('createdAt', {mode: 'timestamp_ms'}).notNull().default(sql`(unixepoch() * 1000)`), 8 34 });
+1 -1
backend/src/jetstream.ts
··· 25 25 26 26 export function readFromFirehose() { 27 27 jetstream.on("account", (e) => { 28 - console.log("account update", e.account.status); 28 + console.log("account update", e.account.did); 29 29 }); 30 30 31 31 jetstream.on("identity", (e) => {
+30 -5
backend/src/main.ts
··· 4 4 * SPDX-License-Identifier: AGPL-3.0-only 5 5 */ 6 6 7 - import {serve} from '@hono/node-server' 7 + import {serve, type ServerType} from '@hono/node-server' 8 8 import {Config} from "./config.ts"; 9 + import {readFromFirehose, startFirehose} from "./jetstream.ts"; 9 10 import app from "./server.ts"; 10 - import {readFromFirehose, startFirehose} from "./jetstream.ts"; 11 + import {Database} from "./db/database.js"; 11 12 12 13 function main() { 13 - console.log("clippr-ts starting...") 14 + console.log("clippr-be starting...") 14 15 15 16 console.log('initializing config') 16 - const config = Config.getInstance(); // Get config from config.toml 17 + const config: Config = Config.getInstance(); // Get config from config.toml 18 + 19 + console.log('initializing database') 20 + Database.getInstance(); 17 21 18 22 console.log('initializing firehose connection'); 19 23 startFirehose(); 20 24 readFromFirehose(); 21 25 22 - serve({ 26 + const server: ServerType = serve({ 23 27 port: config.get("port"), 24 28 hostname: config.get("hostname"), 25 29 fetch: app.fetch, 26 30 }); 27 31 28 32 console.log(`server started at http://${config.get("hostname")}:${config.get("port")}`); 33 + 34 + // TODO: Fix bug where the signals don't get handled by tsx properly and the app gets killed 35 + // const signalPromise = new Promise<void>((resolve) => { 36 + // process.on('SIGINT', () => { 37 + // console.log('\nReceived SIGINT'); 38 + // resolve(); 39 + // }); 40 + // 41 + // process.on('SIGTERM', () => { 42 + // console.log('\nReceived SIGTERM'); 43 + // resolve(); 44 + // }); 45 + // }); 46 + // 47 + // signalPromise.then(() => { 48 + // console.log('shutting down server'); 49 + // server.close(() => { 50 + // console.log('server shut down, bye!'); 51 + // }); 52 + // process.exit(0); 53 + // }); 29 54 } 30 55 31 56 main();
+13 -8
backend/tsconfig.json
··· 3 3 "allowImportingTsExtensions": true, 4 4 "esModuleInterop": true, 5 5 "skipLibCheck": true, 6 - "target": "es2022", 6 + "target": "ESNext", 7 7 "allowJs": true, 8 8 "resolveJsonModule": true, 9 9 "moduleDetection": "force", 10 10 "isolatedModules": true, 11 11 "verbatimModuleSyntax": true, 12 - 12 + "types": [ 13 + "node" 14 + ], 13 15 "strict": true, 14 16 "noUncheckedIndexedAccess": true, 15 17 "noImplicitOverride": true, 16 - 17 - "module": "preserve", 18 + "module": "NodeNext", 18 19 "noEmit": true, 19 - 20 20 "jsx": "react-jsx", 21 21 "jsxImportSource": "hono/jsx", 22 - 23 - "lib": ["es2022"] 24 - } 22 + "outDir": "./dist", 23 + "lib": [ 24 + "ESNext" 25 + ] 26 + }, 27 + "exclude": [ 28 + "node_modules" 29 + ] 25 30 }