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:
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")