From b15a4aac466a6d2d5cbbb5ff01c71c279ac5188e Mon Sep 17 00:00:00 2001
From: Daniel <dhaavi@users.noreply.github.com>
Date: Tue, 23 Apr 2024 10:46:50 +0200
Subject: [PATCH] Add ready API endpoint and temporarily "backport" to ping

---
 api/endpoints_debug.go | 25 +++++++++++++++++++++++++
 modules/start.go       |  5 +++++
 2 files changed, 30 insertions(+)

diff --git a/api/endpoints_debug.go b/api/endpoints_debug.go
index aed729f..2157b70 100644
--- a/api/endpoints_debug.go
+++ b/api/endpoints_debug.go
@@ -3,6 +3,7 @@ package api
 import (
 	"bytes"
 	"context"
+	"errors"
 	"fmt"
 	"net/http"
 	"os"
@@ -11,6 +12,7 @@ import (
 	"time"
 
 	"github.com/safing/portbase/info"
+	"github.com/safing/portbase/modules"
 	"github.com/safing/portbase/utils/debug"
 )
 
@@ -25,6 +27,16 @@ func registerDebugEndpoints() error {
 		return err
 	}
 
+	if err := RegisterEndpoint(Endpoint{
+		Path:        "ready",
+		Read:        PermitAnyone,
+		ActionFunc:  ready,
+		Name:        "Ready",
+		Description: "Check if Portmaster has completed starting and is ready.",
+	}); err != nil {
+		return err
+	}
+
 	if err := RegisterEndpoint(Endpoint{
 		Path:        "debug/stack",
 		Read:        PermitAnyone,
@@ -118,9 +130,22 @@ You can easily view this data in your browser with this command (with Go install
 
 // ping responds with pong.
 func ping(ar *Request) (msg string, err error) {
+	// TODO: Remove upgrade to "ready" when all UI components have transitioned.
+	if modules.IsStarting() || modules.IsShuttingDown() {
+		return "", ErrorWithStatus(errors.New("portmaster is not ready"), http.StatusTooEarly)
+	}
+
 	return "Pong.", nil
 }
 
+// ready checks if Portmaster has completed starting.
+func ready(ar *Request) (msg string, err error) {
+	if modules.IsStarting() || modules.IsShuttingDown() {
+		return "", ErrorWithStatus(errors.New("portmaster is not ready"), http.StatusTooEarly)
+	}
+	return "Portmaster is ready.", nil
+}
+
 // getStack returns the current goroutine stack.
 func getStack(_ *Request) (data []byte, err error) {
 	buf := &bytes.Buffer{}
diff --git a/modules/start.go b/modules/start.go
index d73da5e..74730f2 100644
--- a/modules/start.go
+++ b/modules/start.go
@@ -24,6 +24,11 @@ func SetGlobalPrepFn(fn func() error) {
 	}
 }
 
+// IsStarting returns whether the initial global start is still in progress.
+func IsStarting() bool {
+	return !initialStartCompleted.IsSet()
+}
+
 // Start starts all modules in the correct order. In case of an error, it will automatically shutdown again.
 func Start() error {
 	if !modulesLocked.SetToIf(false, true) {