diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index d5ecac7fa2d..695dea78b15 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -32,7 +32,11 @@ jobs: - run: "go vet ./..." - uses: golangci/golangci-lint-action@v6 with: - args: --timeout 10m --skip-dirs tests --disable errcheck --enable sqlclosecheck --enable misspell --enable gofmt --enable goimports --enable whitespace --enable gocritic + args: > + --timeout 10m + --exclude-dirs=tests + --disable errcheck + --enable sqlclosecheck,misspell,gofmt,goimports,whitespace,gocritic - uses: dominikh/staticcheck-action@v1.3.1 with: version: "2024.1.1" diff --git a/client/client.go b/client/client.go index 11659164fba..a463b2d725a 100644 --- a/client/client.go +++ b/client/client.go @@ -185,6 +185,25 @@ func (c *Client) MarkAllAsRead(userID int64) error { return err } +// FetchIntegrationsStatus fetches the integrations status for a user. +func (c *Client) FetchIntegrationsStatus() (bool, error) { + body, err := c.request.Get("/v1/users/integrations/status") + if err != nil { + return false, err + } + defer body.Close() + + var response struct { + HasIntegrations bool `json:"has_integrations"` + } + + if err := json.NewDecoder(body).Decode(&response); err != nil { + return false, fmt.Errorf("miniflux: response error (%v)", err) + } + + return response.HasIntegrations, nil +} + // Discover try to find subscriptions from a website. func (c *Client) Discover(url string) (Subscriptions, error) { body, err := c.request.Post("/v1/discover", map[string]string{"url": url}) diff --git a/internal/api/api.go b/internal/api/api.go index 48d13039cf6..19d5ba62d0c 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -37,6 +37,7 @@ func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool) { sr.HandleFunc("/users/{userID:[0-9]+}", handler.updateUser).Methods(http.MethodPut) sr.HandleFunc("/users/{userID:[0-9]+}", handler.removeUser).Methods(http.MethodDelete) sr.HandleFunc("/users/{userID:[0-9]+}/mark-all-as-read", handler.markUserAsRead).Methods(http.MethodPut) + sr.HandleFunc("/users/integrations/status", handler.getIntegrationsStatus).Methods(http.MethodGet) sr.HandleFunc("/users/{username}", handler.userByUsername).Methods(http.MethodGet) sr.HandleFunc("/me", handler.currentUser).Methods(http.MethodGet) sr.HandleFunc("/categories", handler.createCategory).Methods(http.MethodPost) diff --git a/internal/api/api_integration_test.go b/internal/api/api_integration_test.go index fe172ce587d..8bbb777a495 100644 --- a/internal/api/api_integration_test.go +++ b/internal/api/api_integration_test.go @@ -2483,6 +2483,32 @@ func TestSaveEntryEndpoint(t *testing.T) { } } +func TestFetchIntegrationsStatusEndpoint(t *testing.T) { + testConfig := newIntegrationTestConfig() + if !testConfig.isConfigured() { + t.Skip(skipIntegrationTestsMessage) + } + + adminClient := miniflux.NewClient(testConfig.testBaseURL, testConfig.testAdminUsername, testConfig.testAdminPassword) + + regularTestUser, err := adminClient.CreateUser(testConfig.genRandomUsername(), testConfig.testRegularPassword, false) + if err != nil { + t.Fatal(err) + } + defer adminClient.DeleteUser(regularTestUser.ID) + + regularUserClient := miniflux.NewClient(testConfig.testBaseURL, regularTestUser.Username, testConfig.testRegularPassword) + + hasIntegrations, err := regularUserClient.FetchIntegrationsStatus() + if err != nil { + t.Fatalf("Failed to fetch integrations status: %v", err) + } + + if hasIntegrations { + t.Fatalf("New user should not have integrations configured") + } +} + func TestFetchContentEndpoint(t *testing.T) { testConfig := newIntegrationTestConfig() if !testConfig.isConfigured() { diff --git a/internal/api/user.go b/internal/api/user.go index 71d70e9c5b5..112c19a25d4 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -130,6 +130,25 @@ func (h *handler) markUserAsRead(w http.ResponseWriter, r *http.Request) { json.NoContent(w, r) } +func (h *handler) getIntegrationsStatus(w http.ResponseWriter, r *http.Request) { + userID := request.UserID(r) + + if _, err := h.store.UserByID(userID); err != nil { + json.NotFound(w, r) + return + } + + hasIntegrations := h.store.HasSaveEntry(userID) + + response := struct { + HasIntegrations bool `json:"has_integrations"` + }{ + HasIntegrations: hasIntegrations, + } + + json.OK(w, r, response) +} + func (h *handler) users(w http.ResponseWriter, r *http.Request) { if !request.IsAdminUser(r) { json.Forbidden(w, r)