everything you need to create an atproto appview

more noodling

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li fa9a6e22 5d5ec78c

verified
+290 -2
+2
.gitignore
··· 3 3 *.db-shm 4 4 *.db-wal 5 5 .env 6 + .envrc 7 + .direnv
+72
appview/pages/home.go
··· 2 2 3 3 import ( 4 4 "context" 5 + "strconv" 5 6 6 7 . "maragu.dev/gomponents" 7 8 . "maragu.dev/gomponents/html" 9 + "tangled.org/oppi.li/atproto-starterkit/appview/config" 8 10 "tangled.org/oppi.li/atproto-starterkit/appview/types" 9 11 ) 10 12 ··· 81 83 A(Href("/login"), Text("Add another account")), 82 84 ), 83 85 }), 86 + ), 87 + 88 + H2(Text("Configuration")), 89 + ConfigTable(p.config), 90 + ) 91 + } 92 + 93 + func ConfigTable(c *config.Config) Node { 94 + borderStyle := Style("border: 1px solid #ddd; padding: 8px;") 95 + 96 + return Table( 97 + THead( 98 + Tr( 99 + Th(borderStyle, Text("Config Key")), 100 + Th(borderStyle, Text("Value")), 101 + ), 102 + ), 103 + TBody( 104 + Tr( 105 + Td(borderStyle, Text("Core.Name")), 106 + Td(borderStyle, Text(c.Core.Name)), 107 + ), 108 + Tr( 109 + Td(borderStyle, Text("Core.Host")), 110 + Td(borderStyle, Text(c.Core.Host)), 111 + ), 112 + Tr( 113 + Td(borderStyle, Text("Core.ListenAddr")), 114 + Td(borderStyle, Text(c.Core.ListenAddr)), 115 + ), 116 + Tr( 117 + Td(borderStyle, Text("Core.DbPath")), 118 + Td(borderStyle, Text(c.Core.DbPath)), 119 + ), 120 + Tr( 121 + Td(borderStyle, Text("Core.Dev")), 122 + Td(borderStyle, Text(strconv.FormatBool(c.Core.Dev))), 123 + ), 124 + Tr( 125 + Td(borderStyle, Text("Core.BaseUrl()")), 126 + Td(borderStyle, Text(c.Core.BaseUrl())), 127 + ), 128 + Tr( 129 + Td(borderStyle, Text("Core.CookieSecret")), 130 + Td(borderStyle, Text(c.Core.CookieSecret)), 131 + ), 132 + Tr( 133 + Td(borderStyle, Text("OAuth.ClientKid")), 134 + Td(borderStyle, Text(c.OAuth.ClientKid)), 135 + ), 136 + Tr( 137 + Td(borderStyle, Text("OAuth.ClientSecret")), 138 + Td(borderStyle, Text(c.OAuth.ClientSecret)), 139 + ), 140 + Tr( 141 + Td(borderStyle, Text("Redis.Addr")), 142 + Td(borderStyle, Text(c.Redis.Addr)), 143 + ), 144 + Tr( 145 + Td(borderStyle, Text("Redis.DB")), 146 + Td(borderStyle, Textf("%d", c.Redis.DB)), 147 + ), 148 + Tr( 149 + Td(borderStyle, Text("Redis.Password")), 150 + Td(borderStyle, Text(c.Redis.Password)), 151 + ), 152 + Tr( 153 + Td(borderStyle, Text("Plc.PLCURL")), 154 + Td(borderStyle, Text(c.Plc.PLCURL)), 155 + ), 84 156 ), 85 157 ) 86 158 }
+4 -1
appview/pages/pages.go
··· 5 5 "net/http" 6 6 7 7 . "maragu.dev/gomponents" 8 + "tangled.org/oppi.li/atproto-starterkit/appview/config" 8 9 "tangled.org/oppi.li/atproto-starterkit/idresolver" 9 10 ) 10 11 11 12 type Pages struct { 12 13 resolver *idresolver.Resolver 14 + config *config.Config 13 15 } 14 16 15 - func New(resolver *idresolver.Resolver) *Pages { 17 + func New(resolver *idresolver.Resolver, cfg *config.Config) *Pages { 16 18 return &Pages{ 17 19 resolver: resolver, 20 + config: cfg, 18 21 } 19 22 } 20 23
+1 -1
appview/server/server.go
··· 41 41 return nil, fmt.Errorf("failed to start oauth handler: %w", err) 42 42 } 43 43 44 - pages := pages.New(res) 44 + pages := pages.New(res, config) 45 45 46 46 server := &Server{ 47 47 d,
+82
flake.lock
··· 1 + { 2 + "nodes": { 3 + "flake-utils": { 4 + "inputs": { 5 + "systems": "systems" 6 + }, 7 + "locked": { 8 + "lastModified": 1731533236, 9 + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 + "owner": "numtide", 11 + "repo": "flake-utils", 12 + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 + "type": "github" 14 + }, 15 + "original": { 16 + "owner": "numtide", 17 + "repo": "flake-utils", 18 + "type": "github" 19 + } 20 + }, 21 + "gomod2nix": { 22 + "inputs": { 23 + "flake-utils": "flake-utils", 24 + "nixpkgs": [ 25 + "nixpkgs" 26 + ] 27 + }, 28 + "locked": { 29 + "lastModified": 1770585520, 30 + "narHash": "sha256-yBz9Ozd5Wb56i3e3cHZ8WcbzCQ9RlVaiW18qDYA/AzA=", 31 + "owner": "nix-community", 32 + "repo": "gomod2nix", 33 + "rev": "1201ddd1279c35497754f016ef33d5e060f3da8d", 34 + "type": "github" 35 + }, 36 + "original": { 37 + "owner": "nix-community", 38 + "repo": "gomod2nix", 39 + "type": "github" 40 + } 41 + }, 42 + "nixpkgs": { 43 + "locked": { 44 + "lastModified": 1771848320, 45 + "narHash": "sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=", 46 + "owner": "nixos", 47 + "repo": "nixpkgs", 48 + "rev": "2fc6539b481e1d2569f25f8799236694180c0993", 49 + "type": "github" 50 + }, 51 + "original": { 52 + "owner": "nixos", 53 + "ref": "nixos-unstable", 54 + "repo": "nixpkgs", 55 + "type": "github" 56 + } 57 + }, 58 + "root": { 59 + "inputs": { 60 + "gomod2nix": "gomod2nix", 61 + "nixpkgs": "nixpkgs" 62 + } 63 + }, 64 + "systems": { 65 + "locked": { 66 + "lastModified": 1681028828, 67 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 68 + "owner": "nix-systems", 69 + "repo": "default", 70 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 71 + "type": "github" 72 + }, 73 + "original": { 74 + "owner": "nix-systems", 75 + "repo": "default", 76 + "type": "github" 77 + } 78 + } 79 + }, 80 + "root": "root", 81 + "version": 7 82 + }
+90
flake.nix
··· 1 + { 2 + description = "atproto starterkit"; 3 + 4 + inputs = { 5 + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 6 + gomod2nix = { 7 + url = "github:nix-community/gomod2nix"; 8 + inputs.nixpkgs.follows = "nixpkgs"; 9 + }; 10 + }; 11 + 12 + outputs = { 13 + self, 14 + nixpkgs, 15 + gomod2nix, 16 + ... 17 + }: let 18 + supportedSystems = ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"]; 19 + forAllSystems = nixpkgs.lib.genAttrs supportedSystems; 20 + nixpkgsFor = forAllSystems (system: nixpkgs.legacyPackages.${system}); 21 + in { 22 + devShells = forAllSystems (system: let 23 + pkgs = nixpkgsFor.${system}; 24 + staticShell = pkgs.mkShell.override { 25 + stdenv = pkgs.pkgsStatic.stdenv; 26 + }; 27 + in { 28 + default = staticShell { 29 + nativeBuildInputs = [ 30 + pkgs.go 31 + pkgs.air 32 + pkgs.gopls 33 + pkgs.httpie 34 + pkgs.litecli 35 + pkgs.websocat 36 + pkgs.tailwindcss 37 + pkgs.nixos-shell 38 + pkgs.redis 39 + pkgs.coreutils # for those of us who are on systems that use busybox (alpine) 40 + gomod2nix.legacyPackages.${system}.gomod2nix 41 + ]; 42 + shellHook = '' 43 + export TANGLED_OAUTH_CLIENT_KID="$(date +%s)" 44 + ''; 45 + env.CGO_ENABLED = 1; 46 + }; 47 + }); 48 + 49 + apps = forAllSystems (system: let 50 + pkgs = nixpkgsFor."${system}"; 51 + in { 52 + lexgen = { 53 + type = "app"; 54 + program = 55 + (pkgs.writeShellApplication { 56 + name = "lexgen"; 57 + text = '' 58 + if ! command -v lexgen > /dev/null; then 59 + echo "error: must be executed from devshell" 60 + exit 1 61 + fi 62 + 63 + rootDir=$(jj --ignore-working-copy root || git rev-parse --show-toplevel) || (echo "error: can't find repo root?"; exit 1) 64 + cd "$rootDir" 65 + 66 + outdir=api/lexicons 67 + 68 + rm -f $outdir/* 69 + lexgen --build-file lexicon-build-config.json lexicons 70 + sed -i.bak 's/\tutil/\/\/\tutil/' $outdir/* 71 + # lexgen generates incomplete Marshaler/Unmarshaler for union types 72 + find $outdir/*.go -not -name "cbor_gen.go" -exec \ 73 + sed -i '/^func.*\(MarshalCBOR\|UnmarshalCBOR\)/,/^}/ s/^/\/\/ /' {} + 74 + ${pkgs.gotools}/bin/goimports -w $outdir/* 75 + go run ./cmd/cborgen/ 76 + lexgen --build-file lexicon-build-config.json lexicons 77 + rm $outdir/*.bak 78 + ''; 79 + }) 80 + + /bin/lexgen; 81 + }; 82 + gomod2nix = { 83 + type = "app"; 84 + program = toString (pkgs.writeShellScript "gomod2nix" '' 85 + ${gomod2nix.legacyPackages.${system}.gomod2nix}/bin/gomod2nix generate --outdir ./nix 86 + ''); 87 + }; 88 + }); 89 + }; 90 + }
+9
lexicon-build-config.json
··· 1 + [ 2 + { 3 + "package": "lexicons", 4 + "prefix": "xyz.statusphere", 5 + "outdir": "api/lexicons", 6 + "import": "tangled.org/oppi.li/atproto-starterkit/api/lexicons", 7 + "gen-server": true 8 + } 9 + ]
+30
lexicons/status.json
··· 1 + { 2 + "id": "xyz.statusphere.status", 3 + "defs": { 4 + "main": { 5 + "key": "tid", 6 + "type": "record", 7 + "record": { 8 + "type": "object", 9 + "required": [ 10 + "status", 11 + "createdAt" 12 + ], 13 + "properties": { 14 + "status": { 15 + "type": "string", 16 + "maxLength": 32, 17 + "minLength": 1, 18 + "maxGraphemes": 1 19 + }, 20 + "createdAt": { 21 + "type": "string", 22 + "format": "datetime" 23 + } 24 + } 25 + } 26 + } 27 + }, 28 + "$type": "com.atproto.lexicon.schema", 29 + "lexicon": 1 30 + }