An implementation of the ATProto statusphere example app but in Go

got things working for deploying

Signed-off-by: Will Andrews <did:plc:dadhhalkfcq3gucaq25hjqon>

Changed files
+25 -15
cmd
database
+14 -4
cmd/main.go
··· 24 24 defaultServerAddr = "wss://jetstream.atproto.tools/subscribe" 25 25 httpClientTimeoutDuration = time.Second * 5 26 26 transportIdleConnTimeoutDuration = time.Second * 90 27 + defaultPort = "8080" 27 28 ) 28 29 29 30 func main() { 30 - err := godotenv.Load(".env") 31 + envLocation := os.Getenv("ENV_LOCATION") 32 + if envLocation == "" { 33 + envLocation = ".env" 34 + } 35 + 36 + err := godotenv.Load(envLocation) 31 37 if err != nil { 32 38 if !os.IsNotExist(err) { 33 39 log.Fatal("Error loading .env file") ··· 62 68 } 63 69 64 70 var config oauth.ClientConfig 65 - bind := ":8080" 71 + port := os.Getenv("PORT") 72 + if port == "" { 73 + port = defaultPort 74 + } 66 75 scopes := []string{"atproto", "transition:generic"} 67 76 if host == "" { 77 + host = fmt.Sprintf("http://127.0.0.1:%s", port) 68 78 config = oauth.NewLocalhostConfig( 69 - fmt.Sprintf("http://127.0.0.1%s/oauth-callback", bind), 79 + fmt.Sprintf("%s/oauth-callback", host), 70 80 scopes, 71 81 ) 72 82 slog.Info("configuring localhost OAuth client", "CallbackURL", config.CallbackURL) ··· 79 89 } 80 90 oauthClient := oauth.NewClientApp(&config, db) 81 91 82 - server, err := statusphere.NewServer(host, 8080, db, oauthClient, httpClient) 92 + server, err := statusphere.NewServer(host, port, db, oauthClient, httpClient) 83 93 if err != nil { 84 94 slog.Error("create new server", "error", err) 85 95 return
-2
database/oauth_sessions.go
··· 48 48 return fmt.Errorf("marshalling scopes: %w", err) 49 49 } 50 50 51 - slog.Info("session to save", "did", sess.AccountDID.String(), "session id", sess.SessionID) 52 - 53 51 sql := `INSERT INTO oauthsessions (accountDID, sessionID, hostURL, authServerURL, authServerTokenEndpoint, scopes, accessToken, refreshToken, dpopAuthServerNonce, dpopHostNonce, dpopPrivateKeyMultibase) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(accountDID) DO NOTHING;` // TODO: update on conflict 54 52 _, err = d.db.Exec(sql, sess.AccountDID.String(), sess.SessionID, sess.HostURL, sess.AuthServerURL, sess.AuthServerTokenEndpoint, string(scopes), sess.AccessToken, sess.RefreshToken, sess.DPoPAuthServerNonce, sess.DPoPHostNonce, sess.DPoPPrivateKeyMultibase) 55 53 if err != nil {
-2
home_handler.go
··· 114 114 return 115 115 } 116 116 117 - slog.Info("session", "did", did.String(), "session id", sessionID) 118 - 119 117 oauthSess, err := s.oauthClient.ResumeSession(r.Context(), *did, sessionID) 120 118 if err != nil { 121 119 http.Error(w, "not authenticated", http.StatusUnauthorized)
+11 -7
server.go
··· 2 2 3 3 import ( 4 4 "context" 5 - _ "embed" 5 + "embed" 6 6 "encoding/json" 7 7 "errors" 8 8 "fmt" ··· 44 44 httpClient *http.Client 45 45 } 46 46 47 - func NewServer(host string, port int, store Store, oauthClient *oauth.ClientApp, httpClient *http.Client) (*Server, error) { 47 + //go:embed html 48 + var htmlFolder embed.FS 49 + 50 + func NewServer(host string, port string, store Store, oauthClient *oauth.ClientApp, httpClient *http.Client) (*Server, error) { 48 51 sessionStore := sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY"))) 49 52 50 - homeTemplate, err := template.ParseFiles("./html/home.html") 53 + homeTemplate, err := template.ParseFS(htmlFolder, "html/home.html") 51 54 if err != nil { 52 - return nil, fmt.Errorf("parsing home template: %w", err) 55 + return nil, fmt.Errorf("error parsing templates: %w", err) 53 56 } 54 - loginTemplate, err := template.ParseFiles("./html/login.html") 57 + 58 + loginTemplate, err := template.ParseFS(htmlFolder, "html/login.html") 55 59 if err != nil { 56 60 return nil, fmt.Errorf("parsing login template: %w", err) 57 61 } ··· 83 87 mux.HandleFunc("/oauth-client-metadata.json", srv.serveClientMetadata) 84 88 mux.HandleFunc("/oauth-callback", srv.handleOauthCallback) 85 89 86 - addr := fmt.Sprintf("0.0.0.0:%d", port) 90 + addr := fmt.Sprintf("0.0.0.0:%s", port) 87 91 srv.httpserver = &http.Server{ 88 92 Addr: addr, 89 93 Handler: mux, ··· 140 144 metadata.ClientName = &clientName 141 145 metadata.ClientURI = &s.host 142 146 if s.oauthClient.Config.IsConfidential() { 143 - jwksURI := fmt.Sprintf("%s/jwks.json", r.Host) 147 + jwksURI := fmt.Sprintf("%s/jwks.json", s.host) 144 148 metadata.JWKSURI = &jwksURI 145 149 } 146 150