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

Configure Feed

Select the types of activity you want to include in your feed.

Getting ready to deploy

+51 -6
+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,