+1
-1
.idea/clippr.iml
+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
+1
-1
README.md
+2
-1
backend/.gitignore
+2
-1
backend/.gitignore
+3
-1
backend/config.example.toml
+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
+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
+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
+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
+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
+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
+1
-1
backend/src/jetstream.ts
+30
-5
backend/src/main.ts
+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
+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
}