A URL shortener service that uses ATProto to allow self hosting and ensuring the user owns their data

Getting ready to deploy

+4
.dockerignore
··· 1 + at-shorter 2 + database.db 3 + .env 4 + example.env
+19
Dockerfile
··· 1 + FROM golang:latest AS builder 2 + 3 + WORKDIR /app 4 + 5 + COPY . . 6 + RUN go mod download 7 + 8 + COPY . . 9 + 10 + RUN CGO_ENABLED=0 go build -o at-shorter . 11 + 12 + FROM alpine:latest 13 + 14 + RUN apk --no-cache add ca-certificates 15 + 16 + WORKDIR /root/ 17 + COPY --from=builder /app/at-shorter . 18 + 19 + CMD ["./at-shorter"]
+14 -4
cmd/atshorter/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") ··· 54 60 defer db.Close() 55 61 56 62 var config oauth.ClientConfig 57 - bind := ":8080" 63 + port := os.Getenv("PORT") 64 + if port == "" { 65 + port = defaultPort 66 + } 67 + 58 68 scopes := []string{ 59 69 "atproto", 60 70 "repo:com.atshorter.shorturl?action=create", ··· 63 73 } 64 74 if host == "" { 65 75 config = oauth.NewLocalhostConfig( 66 - fmt.Sprintf("http://127.0.0.1%s/oauth-callback", bind), 76 + fmt.Sprintf("http://127.0.0.1:%s/oauth-callback", port), 67 77 scopes, 68 78 ) 69 79 slog.Info("configuring localhost OAuth client", "CallbackURL", config.CallbackURL) ··· 83 93 }, 84 94 } 85 95 86 - server, err := atshorter.NewServer(host, 8080, db, oauthClient, httpClient) 96 + server, err := atshorter.NewServer(host, port, db, oauthClient, httpClient) 87 97 if err != nil { 88 98 slog.Error("create new server", "error", err) 89 99 return
+11
docker-compose.yaml
··· 1 + services: 2 + knot: 3 + container_name: at-shorter 4 + image: willdot/at-shorter:latest 5 + environment: 6 + ENV_LOCATION: "/data/at-shorter.env" 7 + volumes: 8 + - ./data:/app/data 9 + ports: 10 + - "3005:3005" 11 + restart: always
+1
example.env
··· 3 3 HOST="the host of the service such as https://my-url-shortner.com" 4 4 DATABASE_PATH="./" 5 5 JS_SERVER_ADDR="set to a different Jetstream instance" 6 + PORT="3002"
+2 -2
server.go
··· 40 40 mu sync.Mutex 41 41 } 42 42 43 - func NewServer(host string, port int, store Store, oauthClient *oauth.ClientApp, httpClient *http.Client) (*Server, error) { 43 + func NewServer(host string, port string, store Store, oauthClient *oauth.ClientApp, httpClient *http.Client) (*Server, error) { 44 44 sessionStore := sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY"))) 45 45 46 46 homeTemplate, err := template.ParseFiles("./html/home.html") ··· 83 83 mux.HandleFunc("GET /oauth-client-metadata.json", srv.serveClientMetadata) 84 84 mux.HandleFunc("GET /oauth-callback", srv.handleOauthCallback) 85 85 86 - addr := fmt.Sprintf("0.0.0.0:%d", port) 86 + addr := fmt.Sprintf("0.0.0.0:%s", port) 87 87 srv.httpserver = &http.Server{ 88 88 Addr: addr, 89 89 Handler: mux,