gimmich

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 340692a4b87a4e21e5091954bba17cdc785d8354
parent d279a8c65adf94748fa063f2c3d2c31ec18db930
Author: Martin Ashby <martin@ashbysoft.com>
Date:   Tue, 29 Sep 2026 00:02:55 +0100

I can get to the app login page without error

Diffstat:
ACaddyfile | 6++++++
Mmain.go | 123++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Munimplemented.go | 20--------------------
3 files changed, 125 insertions(+), 24 deletions(-)

diff --git a/Caddyfile b/Caddyfile @@ -0,0 +1,5 @@ +:80 + +reverse_proxy :8080 + +log +\ No newline at end of file diff --git a/main.go b/main.go @@ -5,18 +5,51 @@ import ( "log" "log/slog" "net/http" + "strconv" + + "database/sql" + "os" "github.com/google/uuid" + _ "github.com/mattn/go-sqlite3" ) //go:generate curl -SsL -o immich-openapi-specs.json https://github.com/immich-app/immich/raw/refs/tags/v3.0.3/open-api/immich-openapi-specs.json //go:generate go tool oapi-codegen -config cfg.yaml immich-openapi-specs.json -const address = "127.0.0.1:8080" +const ( + address = "127.0.0.1:8080" + api = "/api" +) func main() { + db, err := sql.Open("sqlite3", "gimmich.db") + if err != nil { + slog.Error("error opening database", "err", err.Error()) + os.Exit(1) + } + defer func() { + _ = db.Close() + }() + m := http.NewServeMux() + m.HandleFunc("/.well-known/immich", func(w http.ResponseWriter, r *http.Request) { + type Api struct { + Endpoint string `json:"endpoint"` + } + type WellKnown struct { + Api Api `json:"api"` + } + doWriteJsonResponse(w, WellKnown{ + Api: Api{ + Endpoint: api, + }, + }) + }) log.Printf("Starting gimmich on %s", address) - log.Fatal(http.ListenAndServe(address, Handler(&MyServer{}))) + log.Fatal(http.ListenAndServe(address, HandlerWithOptions(&MyServer{}, StdHTTPServerOptions{ + BaseURL: api, + BaseRouter: m, + }))) } func doReadJsonRequestOrSendErr[k any](w http.ResponseWriter, r *http.Request, p *k) (*k, error) { @@ -30,7 +63,18 @@ func doReadJsonRequestOrSendErr[k any](w http.ResponseWriter, r *http.Request, p func doWriteJsonResponse(w http.ResponseWriter, o any) { doWriteResponse(w, func(w http.ResponseWriter) error { - return json.NewEncoder(w).Encode(o) + slog.Info("writing json response...") + res, err := json.Marshal(o) + if err != nil { + serverError(w) + return err + } + h := w.Header() + h.Set("content-length", strconv.Itoa(len(res))) + h.Set("content-type", "application/json") + w.WriteHeader(200) + _, err = w.Write(res) + return err }) } @@ -49,6 +93,14 @@ func badRequest(w http.ResponseWriter) { }) } +func serverError(w http.ResponseWriter) { + w.WriteHeader(500) + doWriteResponse(w, func(w http.ResponseWriter) error { + _, err := w.Write([]byte("server error")) + return err + }) +} + func must[E any](f func() (E, error)) E { e, err := f() if err != nil { @@ -57,7 +109,9 @@ func must[E any](f func() (E, error)) E { return e } -type MyServer struct{} +type MyServer struct{ + +} // Login implements [ServerInterface]. func (m *MyServer) Login(w http.ResponseWriter, r *http.Request) { @@ -85,3 +139,64 @@ func (m *MyServer) Login(w http.ResponseWriter, r *http.Request) { }) } } + +// PingServer implements [ServerInterface]. +func (m *MyServer) PingServer(w http.ResponseWriter, r *http.Request) { + doWriteJsonResponse(w, ServerPingResponse{ + Res: "pong", + }) +} + +// GetServerVersion implements [ServerInterface]. +func (m *MyServer) GetServerVersion(w http.ResponseWriter, r *http.Request) { + doWriteJsonResponse(w, ServerVersionResponseDto{ + Major: 3, + Minor: 2, + Patch: 2, + }) +} + +// GetServerFeatures implements [ServerInterface]. +func (m *MyServer) GetServerFeatures(w http.ResponseWriter, r *http.Request) { + doWriteJsonResponse(w, ServerFeaturesDto{ + ConfigFile: false, + DuplicateDetection: false, + Email: false, + FacialRecognition: false, + ImportFaces: false, + Map: false, + Oauth: false, + OauthAutoLaunch: false, + Ocr: false, + PasswordLogin: true, + RealtimeTranscoding: false, + ReverseGeocoding: false, + Search: false, + Sidecar: false, + SmartSearch: false, + Trash: false, + }) +} + + +// GetServerConfig implements [ServerInterface]. +func (m *MyServer) GetServerConfig(w http.ResponseWriter, r *http.Request) { + hh := r.Header.Get("host") + if hh == "" { + hh = "localhost" + } + doWriteJsonResponse(w, ServerConfigDto{ + ExternalDomain: hh, + IsInitialized: true, + IsOnboarded: true, + LoginPageMessage: "Hello", + MaintenanceMode: false, + MapDarkStyleUrl: "", + MapLightStyleUrl: "", + MinFaces: 0, + OauthButtonText: "", + PublicUsers: false, + TrashDays: 0, + UserDeleteDelay: 0, + }) +} diff --git a/unimplemented.go b/unimplemented.go @@ -651,16 +651,6 @@ func (m *MyServer) GetSegment(w http.ResponseWriter, r *http.Request, id types.U panic("unimplemented") } -// GetServerConfig implements [ServerInterface]. -func (m *MyServer) GetServerConfig(w http.ResponseWriter, r *http.Request) { - panic("unimplemented") -} - -// GetServerFeatures implements [ServerInterface]. -func (m *MyServer) GetServerFeatures(w http.ResponseWriter, r *http.Request) { - panic("unimplemented") -} - // GetServerLicense implements [ServerInterface]. func (m *MyServer) GetServerLicense(w http.ResponseWriter, r *http.Request) { panic("unimplemented") @@ -671,11 +661,6 @@ func (m *MyServer) GetServerStatistics(w http.ResponseWriter, r *http.Request) { panic("unimplemented") } -// GetServerVersion implements [ServerInterface]. -func (m *MyServer) GetServerVersion(w http.ResponseWriter, r *http.Request) { - panic("unimplemented") -} - // GetSessions implements [ServerInterface]. func (m *MyServer) GetSessions(w http.ResponseWriter, r *http.Request) { panic("unimplemented") @@ -851,11 +836,6 @@ func (m *MyServer) MergePerson(w http.ResponseWriter, r *http.Request, id types. panic("unimplemented") } -// PingServer implements [ServerInterface]. -func (m *MyServer) PingServer(w http.ResponseWriter, r *http.Request) { - panic("unimplemented") -} - // PlayAssetVideo implements [ServerInterface]. func (m *MyServer) PlayAssetVideo(w http.ResponseWriter, r *http.Request, id types.UUID, params PlayAssetVideoParams) { panic("unimplemented")