+32
-8
deno.json
+32
-8
deno.json
···
8
8
},
9
9
"lint": {
10
10
"rules": {
11
-
"tags": ["fresh", "recommended"]
11
+
"tags": [
12
+
"fresh",
13
+
"recommended"
14
+
]
12
15
}
13
16
},
14
-
"exclude": ["**/_fresh/*"],
17
+
"exclude": [
18
+
"**/_fresh/*"
19
+
],
15
20
"imports": {
16
21
"@atproto/api": "npm:@atproto/api@^0.15.6",
17
22
"@bigmoves/atproto-oauth-client": "jsr:@bigmoves/atproto-oauth-client@^0.2.0",
···
21
26
"posthog-js": "npm:posthog-js@1.120.0",
22
27
"preact": "npm:preact@^10.26.6",
23
28
"@preact/signals": "npm:@preact/signals@^2.0.4",
24
-
"tailwindcss": "npm:tailwindcss@^3.4.3"
29
+
"tailwindcss": "npm:tailwindcss@^3.4.3",
30
+
"@atproto/crypto": "npm:@atproto/crypto@^0.4.4",
31
+
"@did-plc/lib": "npm:@did-plc/lib@^0.0.4"
25
32
},
26
33
"compilerOptions": {
27
-
"lib": ["dom", "dom.asynciterable", "dom.iterable", "deno.ns"],
34
+
"lib": [
35
+
"dom",
36
+
"dom.asynciterable",
37
+
"dom.iterable",
38
+
"deno.ns"
39
+
],
28
40
"jsx": "precompile",
29
41
"jsxImportSource": "preact",
30
-
"jsxPrecompileSkipElements": ["a", "img", "source", "body", "html", "head"],
31
-
"types": ["node"]
42
+
"jsxPrecompileSkipElements": [
43
+
"a",
44
+
"img",
45
+
"source",
46
+
"body",
47
+
"html",
48
+
"head"
49
+
],
50
+
"types": [
51
+
"node"
52
+
]
32
53
},
33
-
"unstable": ["kv", "otel"]
34
-
}
54
+
"unstable": [
55
+
"kv",
56
+
"otel"
57
+
]
58
+
}
+284
-1
deno.lock
+284
-1
deno.lock
···
33
33
"npm:@atproto/api@*": "0.15.6",
34
34
"npm:@atproto/api@~0.15.6": "0.15.6",
35
35
"npm:@atproto/crypto@*": "0.4.4",
36
+
"npm:@atproto/crypto@~0.4.4": "0.4.4",
36
37
"npm:@atproto/identity@*": "0.4.8",
37
38
"npm:@atproto/jwk@0.1.4": "0.1.4",
38
39
"npm:@atproto/oauth-client@~0.3.13": "0.3.16",
39
40
"npm:@atproto/oauth-types@~0.2.4": "0.2.7",
40
41
"npm:@atproto/syntax@*": "0.4.0",
41
42
"npm:@atproto/xrpc@*": "0.7.0",
43
+
"npm:@did-plc/lib@^0.0.4": "0.0.4",
42
44
"npm:@lucide/lab@*": "0.1.2",
43
45
"npm:@opentelemetry/api@^1.9.0": "1.9.0",
44
46
"npm:@preact/signals@^1.2.3": "1.3.2_preact@10.26.6",
···
293
295
"zod"
294
296
]
295
297
},
298
+
"@atproto/common@0.1.1": {
299
+
"integrity": "sha512-GYwot5wF/z8iYGSPjrLHuratLc0CVgovmwfJss7+BUOB6y2/Vw8+1Vw0n9DDI0gb5vmx3UI8z0uJgC8aa8yuJg==",
300
+
"dependencies": [
301
+
"@ipld/dag-cbor",
302
+
"multiformats@9.9.0",
303
+
"pino",
304
+
"zod"
305
+
]
306
+
},
307
+
"@atproto/crypto@0.1.0": {
308
+
"integrity": "sha512-9xgFEPtsCiJEPt9o3HtJT30IdFTGw5cQRSJVIy5CFhqBA4vDLcdXiRDLCjkzHEVbtNCsHUW6CrlfOgbeLPcmcg==",
309
+
"dependencies": [
310
+
"@noble/secp256k1",
311
+
"big-integer",
312
+
"multiformats@9.9.0",
313
+
"one-webcrypto",
314
+
"uint8arrays@3.0.0"
315
+
]
316
+
},
296
317
"@atproto/crypto@0.4.4": {
297
318
"integrity": "sha512-Yq9+crJ7WQl7sxStVpHgie5Z51R05etaK9DLWYG/7bR5T4bhdcIgF6IfklLShtZwLYdVVj+K15s0BqW9a8PSDA==",
298
319
"dependencies": [
···
311
332
"integrity": "sha512-Z0sLnJ87SeNdAifT+rqpgE1Rc3layMMW25gfWNo4u40RGuRODbdfAZlTwBSU2r+Vk45hU+iE+xeQspfednCEnA==",
312
333
"dependencies": [
313
334
"@atproto/common-web",
314
-
"@atproto/crypto"
335
+
"@atproto/crypto@0.4.4"
315
336
]
316
337
},
317
338
"@atproto/jwk@0.1.4": {
···
369
390
"integrity": "sha512-SfhP9dGx2qclaScFDb58Jnrmim5nk4geZXCqg6sB0I/KZhZEkr9iIx1hLCp+sxkIfEsmEJjeWO4B0rjUIJW5cw==",
370
391
"dependencies": [
371
392
"@atproto/lexicon",
393
+
"zod"
394
+
]
395
+
},
396
+
"@did-plc/lib@0.0.4": {
397
+
"integrity": "sha512-Omeawq3b8G/c/5CtkTtzovSOnWuvIuCI4GTJNrt1AmCskwEQV7zbX5d6km1mjJNbE0gHuQPTVqZxLVqetNbfwA==",
398
+
"dependencies": [
399
+
"@atproto/common",
400
+
"@atproto/crypto@0.1.0",
401
+
"@ipld/dag-cbor",
402
+
"axios",
403
+
"multiformats@9.9.0",
404
+
"uint8arrays@3.0.0",
372
405
"zod"
373
406
]
374
407
},
···
492
525
"os": ["win32"],
493
526
"cpu": ["x64"]
494
527
},
528
+
"@ipld/dag-cbor@7.0.3": {
529
+
"integrity": "sha512-1VVh2huHsuohdXC1bGJNE8WR72slZ9XE2T3wbBBq31dm7ZBatmKLLxrB+XAqafxfRFjv08RZmj/W/ZqaM13AuA==",
530
+
"dependencies": [
531
+
"cborg",
532
+
"multiformats@9.9.0"
533
+
]
534
+
},
495
535
"@isaacs/cliui@8.0.2": {
496
536
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
497
537
"dependencies": [
···
538
578
},
539
579
"@noble/hashes@1.8.0": {
540
580
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="
581
+
},
582
+
"@noble/secp256k1@1.7.2": {
583
+
"integrity": "sha512-/qzwYl5eFLH8OWIecQWM31qld2g1NfjgylK+TNhqtaUKP37Nm+Y+z30Fjhw0Ct8p9yCQEm2N3W/AckdIb3SMcQ=="
541
584
},
542
585
"@nodelib/fs.scandir@2.1.5": {
543
586
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
···
588
631
"undici-types"
589
632
]
590
633
},
634
+
"abort-controller@3.0.0": {
635
+
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
636
+
"dependencies": [
637
+
"event-target-shim"
638
+
]
639
+
},
591
640
"ansi-regex@5.0.1": {
592
641
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
593
642
},
···
616
665
"arg@5.0.2": {
617
666
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
618
667
},
668
+
"asynckit@0.4.0": {
669
+
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
670
+
},
671
+
"atomic-sleep@1.0.0": {
672
+
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="
673
+
},
619
674
"autoprefixer@10.4.17_postcss@8.4.35": {
620
675
"integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==",
621
676
"dependencies": [
···
632
687
"await-lock@2.2.2": {
633
688
"integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw=="
634
689
},
690
+
"axios@1.10.0": {
691
+
"integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
692
+
"dependencies": [
693
+
"follow-redirects",
694
+
"form-data",
695
+
"proxy-from-env"
696
+
]
697
+
},
635
698
"balanced-match@1.0.2": {
636
699
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
700
+
},
701
+
"base64-js@1.5.1": {
702
+
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
703
+
},
704
+
"big-integer@1.6.52": {
705
+
"integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg=="
637
706
},
638
707
"binary-extensions@2.3.0": {
639
708
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="
···
663
732
],
664
733
"bin": true
665
734
},
735
+
"buffer@6.0.3": {
736
+
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
737
+
"dependencies": [
738
+
"base64-js",
739
+
"ieee754"
740
+
]
741
+
},
742
+
"call-bind-apply-helpers@1.0.2": {
743
+
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
744
+
"dependencies": [
745
+
"es-errors",
746
+
"function-bind"
747
+
]
748
+
},
666
749
"camelcase-css@2.0.1": {
667
750
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
668
751
},
···
677
760
},
678
761
"caniuse-lite@1.0.30001717": {
679
762
"integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw=="
763
+
},
764
+
"cborg@1.10.2": {
765
+
"integrity": "sha512-b3tFPA9pUr2zCUiCfRd2+wok2/LBSNUMKOuRRok+WlvvAgEt/PlbgPTsZUcwCOs53IJvLgTp0eotwtosE6njug==",
766
+
"bin": true
680
767
},
681
768
"chokidar@3.6.0": {
682
769
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
···
705
792
"colord@2.9.3": {
706
793
"integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
707
794
},
795
+
"combined-stream@1.0.8": {
796
+
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
797
+
"dependencies": [
798
+
"delayed-stream"
799
+
]
800
+
},
708
801
"commander@4.1.1": {
709
802
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="
710
803
},
···
819
912
"css-tree@2.2.1"
820
913
]
821
914
},
915
+
"delayed-stream@1.0.0": {
916
+
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
917
+
},
822
918
"didyoumean@1.2.2": {
823
919
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
824
920
},
···
850
946
"domhandler"
851
947
]
852
948
},
949
+
"dunder-proto@1.0.1": {
950
+
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
951
+
"dependencies": [
952
+
"call-bind-apply-helpers",
953
+
"es-errors",
954
+
"gopd"
955
+
]
956
+
},
853
957
"eastasianwidth@0.2.0": {
854
958
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
855
959
},
···
865
969
"entities@4.5.0": {
866
970
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
867
971
},
972
+
"es-define-property@1.0.1": {
973
+
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="
974
+
},
975
+
"es-errors@1.3.0": {
976
+
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
977
+
},
978
+
"es-object-atoms@1.1.1": {
979
+
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
980
+
"dependencies": [
981
+
"es-errors"
982
+
]
983
+
},
984
+
"es-set-tostringtag@2.1.0": {
985
+
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
986
+
"dependencies": [
987
+
"es-errors",
988
+
"get-intrinsic",
989
+
"has-tostringtag",
990
+
"hasown"
991
+
]
992
+
},
868
993
"esbuild-wasm@0.23.1": {
869
994
"integrity": "sha512-L3vn7ctvBrtScRfoB0zG1eOCiV4xYvpLYWfe6PDZuV+iDFDm4Mt3xeLIDllG8cDHQ8clUouK3XekulE+cxgkgw==",
870
995
"bin": true
···
903
1028
"escalade@3.2.0": {
904
1029
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="
905
1030
},
1031
+
"event-target-shim@5.0.1": {
1032
+
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
1033
+
},
1034
+
"events@3.3.0": {
1035
+
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
1036
+
},
906
1037
"fast-glob@3.3.3": {
907
1038
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
908
1039
"dependencies": [
···
913
1044
"micromatch"
914
1045
]
915
1046
},
1047
+
"fast-redact@3.5.0": {
1048
+
"integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A=="
1049
+
},
916
1050
"fastq@1.19.1": {
917
1051
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
918
1052
"dependencies": [
···
928
1062
"to-regex-range"
929
1063
]
930
1064
},
1065
+
"follow-redirects@1.15.9": {
1066
+
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
1067
+
},
931
1068
"foreground-child@3.3.1": {
932
1069
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
933
1070
"dependencies": [
···
935
1072
"signal-exit"
936
1073
]
937
1074
},
1075
+
"form-data@4.0.3": {
1076
+
"integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==",
1077
+
"dependencies": [
1078
+
"asynckit",
1079
+
"combined-stream",
1080
+
"es-set-tostringtag",
1081
+
"hasown",
1082
+
"mime-types"
1083
+
]
1084
+
},
938
1085
"fraction.js@4.3.7": {
939
1086
"integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="
940
1087
},
···
946
1093
"function-bind@1.1.2": {
947
1094
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
948
1095
},
1096
+
"get-intrinsic@1.3.0": {
1097
+
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
1098
+
"dependencies": [
1099
+
"call-bind-apply-helpers",
1100
+
"es-define-property",
1101
+
"es-errors",
1102
+
"es-object-atoms",
1103
+
"function-bind",
1104
+
"get-proto",
1105
+
"gopd",
1106
+
"has-symbols",
1107
+
"hasown",
1108
+
"math-intrinsics"
1109
+
]
1110
+
},
1111
+
"get-proto@1.0.1": {
1112
+
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
1113
+
"dependencies": [
1114
+
"dunder-proto",
1115
+
"es-object-atoms"
1116
+
]
1117
+
},
949
1118
"glob-parent@5.1.2": {
950
1119
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
951
1120
"dependencies": [
···
970
1139
],
971
1140
"bin": true
972
1141
},
1142
+
"gopd@1.2.0": {
1143
+
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
1144
+
},
973
1145
"graphemer@1.4.0": {
974
1146
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
975
1147
},
1148
+
"has-symbols@1.1.0": {
1149
+
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="
1150
+
},
1151
+
"has-tostringtag@1.0.2": {
1152
+
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
1153
+
"dependencies": [
1154
+
"has-symbols"
1155
+
]
1156
+
},
976
1157
"hasown@2.0.2": {
977
1158
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
978
1159
"dependencies": [
979
1160
"function-bind"
980
1161
]
1162
+
},
1163
+
"ieee754@1.2.1": {
1164
+
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
981
1165
},
982
1166
"ipaddr.js@2.2.0": {
983
1167
"integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA=="
···
1063
1247
"preact@10.26.6"
1064
1248
]
1065
1249
},
1250
+
"math-intrinsics@1.1.0": {
1251
+
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="
1252
+
},
1066
1253
"mdn-data@2.0.28": {
1067
1254
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="
1068
1255
},
···
1079
1266
"picomatch"
1080
1267
]
1081
1268
},
1269
+
"mime-db@1.52.0": {
1270
+
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
1271
+
},
1272
+
"mime-types@2.1.35": {
1273
+
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1274
+
"dependencies": [
1275
+
"mime-db"
1276
+
]
1277
+
},
1082
1278
"minimatch@9.0.5": {
1083
1279
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
1084
1280
"dependencies": [
···
1127
1323
"object-hash@3.0.0": {
1128
1324
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="
1129
1325
},
1326
+
"on-exit-leak-free@2.1.2": {
1327
+
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="
1328
+
},
1329
+
"one-webcrypto@1.0.3": {
1330
+
"integrity": "sha512-fu9ywBVBPx0gS9K0etIROTiCkvI5S1TDjFsYFb3rC1ewFxeOqsbzq7aIMBHsYfrTHBcGXJaONXXjTl8B01cW1Q=="
1331
+
},
1130
1332
"package-json-from-dist@1.0.1": {
1131
1333
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
1132
1334
},
···
1152
1354
"pify@2.3.0": {
1153
1355
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
1154
1356
},
1357
+
"pino-abstract-transport@1.2.0": {
1358
+
"integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==",
1359
+
"dependencies": [
1360
+
"readable-stream",
1361
+
"split2"
1362
+
]
1363
+
},
1364
+
"pino-std-serializers@6.2.2": {
1365
+
"integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA=="
1366
+
},
1367
+
"pino@8.21.0": {
1368
+
"integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==",
1369
+
"dependencies": [
1370
+
"atomic-sleep",
1371
+
"fast-redact",
1372
+
"on-exit-leak-free",
1373
+
"pino-abstract-transport",
1374
+
"pino-std-serializers",
1375
+
"process-warning",
1376
+
"quick-format-unescaped",
1377
+
"real-require",
1378
+
"safe-stable-stringify",
1379
+
"sonic-boom",
1380
+
"thread-stream"
1381
+
],
1382
+
"bin": true
1383
+
},
1155
1384
"pirates@4.0.7": {
1156
1385
"integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="
1157
1386
},
···
1451
1680
"preact@10.26.7": {
1452
1681
"integrity": "sha512-43xS+QYc1X1IPbw03faSgY6I6OYWcLrJRv3hU0+qMOfh/XCHcP0MX2CVjNARYR2cC/guu975sta4OcjlczxD7g=="
1453
1682
},
1683
+
"process-warning@3.0.0": {
1684
+
"integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ=="
1685
+
},
1686
+
"process@0.11.10": {
1687
+
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
1688
+
},
1689
+
"proxy-from-env@1.1.0": {
1690
+
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
1691
+
},
1454
1692
"psl@1.15.0": {
1455
1693
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
1456
1694
"dependencies": [
···
1463
1701
"queue-microtask@1.2.3": {
1464
1702
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
1465
1703
},
1704
+
"quick-format-unescaped@4.0.4": {
1705
+
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
1706
+
},
1466
1707
"read-cache@1.0.0": {
1467
1708
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
1468
1709
"dependencies": [
1469
1710
"pify"
1470
1711
]
1471
1712
},
1713
+
"readable-stream@4.7.0": {
1714
+
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
1715
+
"dependencies": [
1716
+
"abort-controller",
1717
+
"buffer",
1718
+
"events",
1719
+
"process",
1720
+
"string_decoder"
1721
+
]
1722
+
},
1472
1723
"readdirp@3.6.0": {
1473
1724
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1474
1725
"dependencies": [
1475
1726
"picomatch"
1476
1727
]
1728
+
},
1729
+
"real-require@0.2.0": {
1730
+
"integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="
1477
1731
},
1478
1732
"resolve@1.22.10": {
1479
1733
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
···
1493
1747
"queue-microtask"
1494
1748
]
1495
1749
},
1750
+
"safe-buffer@5.2.1": {
1751
+
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
1752
+
},
1753
+
"safe-stable-stringify@2.5.0": {
1754
+
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="
1755
+
},
1496
1756
"shebang-command@2.0.0": {
1497
1757
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
1498
1758
"dependencies": [
···
1504
1764
},
1505
1765
"signal-exit@4.1.0": {
1506
1766
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="
1767
+
},
1768
+
"sonic-boom@3.8.1": {
1769
+
"integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==",
1770
+
"dependencies": [
1771
+
"atomic-sleep"
1772
+
]
1507
1773
},
1508
1774
"source-map-js@1.2.1": {
1509
1775
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
1510
1776
},
1777
+
"split2@4.2.0": {
1778
+
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="
1779
+
},
1511
1780
"string-width@4.2.3": {
1512
1781
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
1513
1782
"dependencies": [
···
1522
1791
"eastasianwidth",
1523
1792
"emoji-regex@9.2.2",
1524
1793
"strip-ansi@7.1.0"
1794
+
]
1795
+
},
1796
+
"string_decoder@1.3.0": {
1797
+
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
1798
+
"dependencies": [
1799
+
"safe-buffer"
1525
1800
]
1526
1801
},
1527
1802
"strip-ansi@6.0.1": {
···
1613
1888
"any-promise"
1614
1889
]
1615
1890
},
1891
+
"thread-stream@2.7.0": {
1892
+
"integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==",
1893
+
"dependencies": [
1894
+
"real-require"
1895
+
]
1896
+
},
1616
1897
"tlds@1.258.0": {
1617
1898
"integrity": "sha512-XGhStWuOlBA5D8QnyN2xtgB2cUOdJ3ztisne1DYVWMcVH29qh8eQIpRmP3HnuJLdgyzG0HpdGzRMu1lm/Oictw==",
1618
1899
"bin": true
···
1709
1990
"jsr:@fresh/plugin-tailwind@^0.0.1-alpha.7",
1710
1991
"jsr:@knotbin/posthog-fresh@~0.1.3",
1711
1992
"npm:@atproto/api@~0.15.6",
1993
+
"npm:@atproto/crypto@~0.4.4",
1994
+
"npm:@did-plc/lib@^0.0.4",
1712
1995
"npm:@preact/signals@^2.0.4",
1713
1996
"npm:posthog-js@1.120.0",
1714
1997
"npm:preact@^10.26.6",
+201
islands/DidPlcProgress.tsx
+201
islands/DidPlcProgress.tsx
···
1
+
import { useState } from "preact/hooks";
2
+
3
+
interface PlcUpdateStep {
4
+
name: string;
5
+
status: "pending" | "in-progress" | "completed" | "error";
6
+
error?: string;
7
+
}
8
+
9
+
export default function PlcUpdateProgress() {
10
+
const [hasStarted, setHasStarted] = useState(false);
11
+
const [steps, setSteps] = useState<PlcUpdateStep[]>([
12
+
{ name: "Generate PLC key", status: "pending" },
13
+
{ name: "Update PLC key", status: "pending" },
14
+
]);
15
+
const [generatedKey, setGeneratedKey] = useState<string>("");
16
+
const [updateKey, setUpdateKey] = useState<string>("");
17
+
const [updateResult, setUpdateResult] = useState<string>("");
18
+
19
+
const updateStepStatus = (
20
+
index: number,
21
+
status: PlcUpdateStep["status"],
22
+
error?: string
23
+
) => {
24
+
setSteps((prevSteps) =>
25
+
prevSteps.map((step, i) =>
26
+
i === index ? { ...step, status, error } : step
27
+
)
28
+
);
29
+
};
30
+
31
+
const handleStart = () => {
32
+
setHasStarted(true);
33
+
};
34
+
35
+
const handleGenerateKey = async () => {
36
+
updateStepStatus(0, "in-progress");
37
+
try {
38
+
const res = await fetch("/api/plc/keys");
39
+
const text = await res.text();
40
+
if (!res.ok) {
41
+
try {
42
+
const json = JSON.parse(text);
43
+
throw new Error(json.message || "Failed to generate key");
44
+
} catch {
45
+
throw new Error(text || "Failed to generate key");
46
+
}
47
+
}
48
+
let data;
49
+
try {
50
+
data = JSON.parse(text);
51
+
} catch {
52
+
throw new Error("Invalid response from /api/plc/keys");
53
+
}
54
+
if (!data.did || !data.signature) {
55
+
throw new Error("Key generation failed: missing did or signature");
56
+
}
57
+
setGeneratedKey(data.did);
58
+
setUpdateKey(data.did);
59
+
updateStepStatus(0, "completed");
60
+
} catch (error) {
61
+
updateStepStatus(
62
+
0,
63
+
"error",
64
+
error instanceof Error ? error.message : String(error)
65
+
);
66
+
}
67
+
};
68
+
69
+
const handleUpdateKey = async () => {
70
+
updateStepStatus(1, "in-progress");
71
+
setUpdateResult("");
72
+
try {
73
+
const res = await fetch("/api/plc/update", {
74
+
method: "POST",
75
+
headers: { "Content-Type": "application/json" },
76
+
body: JSON.stringify({ key: updateKey }),
77
+
});
78
+
const text = await res.text();
79
+
if (!res.ok) {
80
+
try {
81
+
const json = JSON.parse(text);
82
+
throw new Error(json.message || "Failed to update key");
83
+
} catch {
84
+
throw new Error(text || "Failed to update key");
85
+
}
86
+
}
87
+
setUpdateResult("Key updated successfully!");
88
+
updateStepStatus(1, "completed");
89
+
} catch (error) {
90
+
updateStepStatus(
91
+
1,
92
+
"error",
93
+
error instanceof Error ? error.message : String(error)
94
+
);
95
+
setUpdateResult(error instanceof Error ? error.message : String(error));
96
+
}
97
+
};
98
+
99
+
if (!hasStarted) {
100
+
return (
101
+
<div class="space-y-6">
102
+
<div class="bg-blue-50 dark:bg-blue-900 p-6 rounded-lg border border-blue-200 dark:border-blue-800">
103
+
<h3 class="text-lg font-medium text-blue-900 dark:text-blue-100 mb-4">
104
+
PLC Key Management
105
+
</h3>
106
+
<p class="text-blue-800 dark:text-blue-200 mb-4">
107
+
This tool will help you generate and update PLC (Personal Linked
108
+
Data) keys for your DID (Decentralized Identifier).
109
+
</p>
110
+
<div class="space-y-2 text-sm text-blue-700 dark:text-blue-300">
111
+
<p>
112
+
• Generate a new PLC key with cryptographic signature verification
113
+
</p>
114
+
<p>• Update your existing DID with the new key</p>
115
+
<p>• All operations require authentication</p>
116
+
</div>
117
+
<button
118
+
onClick={handleStart}
119
+
class="mt-4 px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors duration-200"
120
+
>
121
+
Start PLC Key Management
122
+
</button>
123
+
</div>
124
+
</div>
125
+
);
126
+
}
127
+
128
+
return (
129
+
<div class="space-y-8">
130
+
<div class="space-y-4">
131
+
{/* Step 1: Generate PLC key */}
132
+
<div
133
+
class={`flex items-center space-x-3 p-4 rounded-lg ${
134
+
steps[0].status === "completed"
135
+
? "bg-green-50 dark:bg-green-900"
136
+
: steps[0].status === "in-progress"
137
+
? "bg-blue-50 dark:bg-blue-900"
138
+
: steps[0].status === "error"
139
+
? "bg-red-50 dark:bg-red-900"
140
+
: "bg-gray-50 dark:bg-gray-800"
141
+
}`}
142
+
>
143
+
<button
144
+
class="px-4 py-2 bg-blue-600 text-white rounded-md"
145
+
onClick={handleGenerateKey}
146
+
disabled={steps[0].status === "in-progress"}
147
+
>
148
+
Generate PLC Key
149
+
</button>
150
+
{steps[0].status === "completed" && (
151
+
<span class="text-green-700 ml-4">Key generated!</span>
152
+
)}
153
+
{steps[0].status === "error" && (
154
+
<span class="text-red-700 ml-4">{steps[0].error}</span>
155
+
)}
156
+
</div>
157
+
{generatedKey && (
158
+
<div class="p-2 bg-gray-100 dark:bg-gray-700 rounded">
159
+
<div class="text-xs text-gray-700 dark:text-gray-200 break-all">
160
+
<b>Generated DID:</b> {generatedKey}
161
+
</div>
162
+
</div>
163
+
)}
164
+
{/* Step 2: Update PLC key */}
165
+
<div
166
+
class={`flex flex-col space-y-2 p-4 rounded-lg ${
167
+
steps[1].status === "completed"
168
+
? "bg-green-50 dark:bg-green-900"
169
+
: steps[1].status === "in-progress"
170
+
? "bg-blue-50 dark:bg-blue-900"
171
+
: steps[1].status === "error"
172
+
? "bg-red-50 dark:bg-red-900"
173
+
: "bg-gray-50 dark:bg-gray-800"
174
+
}`}
175
+
>
176
+
<label class="text-sm mb-1">DID to update:</label>
177
+
<input
178
+
class="p-2 rounded border border-gray-300 dark:border-gray-600"
179
+
type="text"
180
+
value={updateKey}
181
+
onInput={(e) => setUpdateKey(e.currentTarget.value)}
182
+
placeholder="Paste or use generated DID"
183
+
/>
184
+
<button
185
+
class="mt-2 px-4 py-2 bg-blue-600 text-white rounded-md"
186
+
onClick={handleUpdateKey}
187
+
disabled={steps[1].status === "in-progress" || !updateKey}
188
+
>
189
+
Update PLC Key
190
+
</button>
191
+
{steps[1].status === "completed" && (
192
+
<span class="text-green-700 mt-2">{updateResult}</span>
193
+
)}
194
+
{steps[1].status === "error" && (
195
+
<span class="text-red-700 mt-2">{steps[1].error}</span>
196
+
)}
197
+
</div>
198
+
</div>
199
+
</div>
200
+
);
201
+
}
+42
routes/api/plc/keys.ts
+42
routes/api/plc/keys.ts
···
1
+
import { Secp256k1Keypair } from "@atproto/crypto";
2
+
import { getSessionAgent } from "../../../lib/sessions.ts";
3
+
import { define } from "../../../utils.ts";
4
+
5
+
/**
6
+
* Generate and return PLC keys for the authenticated user
7
+
*/
8
+
export const handler = define.handlers({
9
+
async GET(ctx) {
10
+
const agent = await getSessionAgent(ctx.req);
11
+
if (!agent) {
12
+
return new Response("Unauthorized", { status: 401 });
13
+
}
14
+
15
+
// Create a new keypair
16
+
const keypair = await Secp256k1Keypair.create({ exportable: true });
17
+
18
+
// sign binary data, resulting signature bytes.
19
+
// SHA-256 hash of data is what actually gets signed.
20
+
// signature output is often base64-encoded.
21
+
const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
22
+
const sig = await keypair.sign(data);
23
+
24
+
// serialize the public key as a did:key string, which includes key type metadata
25
+
const pubDidKey = keypair.did();
26
+
console.log(pubDidKey);
27
+
28
+
// output would look something like: 'did:key:zQ3shVRtgqTRHC7Lj4DYScoDgReNpsDp3HBnuKBKt1FSXKQ38'
29
+
30
+
// Return the key information
31
+
return new Response(
32
+
JSON.stringify({
33
+
did: pubDidKey,
34
+
signature: btoa(String.fromCharCode(...sig)),
35
+
data: Array.from(data),
36
+
}),
37
+
{
38
+
headers: { "Content-Type": "application/json" },
39
+
}
40
+
);
41
+
},
42
+
});
+115
routes/api/plc/update.ts
+115
routes/api/plc/update.ts
···
1
+
import { Agent } from "@atproto/api";
2
+
import { getSessionAgent } from "../../../lib/sessions.ts";
3
+
import { define } from "../../../utils.ts";
4
+
import * as plc from "@did-plc/lib";
5
+
6
+
/**
7
+
* Update PLC rotation keys for the authenticated user
8
+
*/
9
+
export const handler = define.handlers({
10
+
async POST(ctx) {
11
+
try {
12
+
const { key: newKey } = await ctx.req.json();
13
+
14
+
if (!newKey) {
15
+
return new Response(
16
+
JSON.stringify({
17
+
success: false,
18
+
message: "Missing key in request body",
19
+
}),
20
+
{
21
+
status: 400,
22
+
headers: { "Content-Type": "application/json" },
23
+
}
24
+
);
25
+
}
26
+
27
+
const agent = await getSessionAgent(ctx.req);
28
+
if (!agent) {
29
+
return new Response(
30
+
JSON.stringify({
31
+
success: false,
32
+
message: "Unauthorized",
33
+
}),
34
+
{
35
+
status: 401,
36
+
headers: { "Content-Type": "application/json" },
37
+
}
38
+
);
39
+
}
40
+
41
+
const did = agent.did;
42
+
if (!did) {
43
+
return new Response(
44
+
JSON.stringify({
45
+
success: false,
46
+
message: "No DID found in session",
47
+
}),
48
+
{
49
+
status: 400,
50
+
headers: { "Content-Type": "application/json" },
51
+
}
52
+
);
53
+
}
54
+
55
+
const client = new plc.Client("https://plc.directory");
56
+
57
+
// Fetch current DID document
58
+
const didDoc = await client.getDocumentData(did);
59
+
if (!didDoc) {
60
+
return new Response(
61
+
JSON.stringify({
62
+
success: false,
63
+
message: "DID document not found",
64
+
}),
65
+
{
66
+
status: 404,
67
+
headers: { "Content-Type": "application/json" },
68
+
}
69
+
);
70
+
}
71
+
72
+
// Create new rotation keys array with the new key at the beginning
73
+
const newKeys = [newKey, ...didDoc.rotationKeys];
74
+
75
+
// Create the update operation
76
+
const updateOp = plc.updateRotationKeysOp(
77
+
did,
78
+
didDoc.rotationKeys,
79
+
newKeys
80
+
);
81
+
82
+
// Submit the operation to the PLC directory
83
+
await client.sendOperation(updateOp);
84
+
85
+
return new Response(
86
+
JSON.stringify({
87
+
success: true,
88
+
message: "PLC rotation keys updated successfully",
89
+
did,
90
+
newKey,
91
+
totalKeys: newKeys.length,
92
+
}),
93
+
{
94
+
status: 200,
95
+
headers: { "Content-Type": "application/json" },
96
+
}
97
+
);
98
+
} catch (error) {
99
+
console.error("PLC update error:", error);
100
+
const message =
101
+
error instanceof Error ? error.message : "Unknown error occurred";
102
+
103
+
return new Response(
104
+
JSON.stringify({
105
+
success: false,
106
+
message: `Failed to update PLC keys: ${message}`,
107
+
}),
108
+
{
109
+
status: 500,
110
+
headers: { "Content-Type": "application/json" },
111
+
}
112
+
);
113
+
}
114
+
},
115
+
});
+19
routes/ticket-booth/index.tsx
+19
routes/ticket-booth/index.tsx
···
1
+
import { PageProps } from "fresh";
2
+
import MigrationSetup from "../../islands/MigrationSetup.tsx";
3
+
import DidPlcProgress from "../../islands/DidPlcProgress.tsx";
4
+
5
+
export default function TicketBooth(props: PageProps) {
6
+
const service = props.url.searchParams.get("service");
7
+
const handle = props.url.searchParams.get("handle");
8
+
9
+
return (
10
+
<div class=" bg-gray-50 dark:bg-gray-900 p-4">
11
+
<div class="max-w-2xl mx-auto">
12
+
<h1 class="font-mono text-3xl font-bold text-gray-900 dark:text-white mb-8">
13
+
Ticket Booth Self-Service Kiosk
14
+
</h1>
15
+
<DidPlcProgress />
16
+
</div>
17
+
</div>
18
+
);
19
+
}