mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-06-05 11:00:13 +00:00
Compare commits
4 Commits
8b28e0c3e4
...
35cd3b9ce5
| Author | SHA1 | Date | |
|---|---|---|---|
| 35cd3b9ce5 | |||
| 90145dd774 | |||
| 9c0fe751cc | |||
| bc7c604a7d |
@@ -80,5 +80,17 @@
|
|||||||
"profileScopeDescription": "Allows the app to access your profile information.",
|
"profileScopeDescription": "Allows the app to access your profile information.",
|
||||||
"groupsScopeName": "Groups",
|
"groupsScopeName": "Groups",
|
||||||
"groupsScopeDescription": "Allows the app to access your group information.",
|
"groupsScopeDescription": "Allows the app to access your group information.",
|
||||||
"backToLoginButton": "Back to login"
|
"backToLoginButton": "Back to login",
|
||||||
|
"phoneScopeName": "Phone",
|
||||||
|
"phoneScopeDescription": "Allows the app to access your phone number.",
|
||||||
|
"addressScopeName": "Address",
|
||||||
|
"addressScopeDescription": "Allows the app to access your address.",
|
||||||
|
"loginTailscaleTitle": "Continue with Tailscale",
|
||||||
|
"loginTailscaleDescription": "We detected that you are accessing Tinyauth from an authorized Tailscale device. Would you like to continue with your Tailscale connection?",
|
||||||
|
"loginTailscaleDeviceName": "Device name:",
|
||||||
|
"loginTailscaleSubmit": "Continue with Tailscale",
|
||||||
|
"loginTailscaleOtherMethod": "Login with another method",
|
||||||
|
"loginTailscaleSuccess": "Successfully authenticated with Tailscale.",
|
||||||
|
"loginTailscaleFail": "Failed to authenticate with Tailscale. Please try again or use another login method.",
|
||||||
|
"logoutTailscaleSubtitle": "You are currently logged in with Tailscale on your device <code>{{deviceName}}</code>. Click the button below to logout."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,5 +84,13 @@
|
|||||||
"phoneScopeName": "Phone",
|
"phoneScopeName": "Phone",
|
||||||
"phoneScopeDescription": "Allows the app to access your phone number.",
|
"phoneScopeDescription": "Allows the app to access your phone number.",
|
||||||
"addressScopeName": "Address",
|
"addressScopeName": "Address",
|
||||||
"addressScopeDescription": "Allows the app to access your address."
|
"addressScopeDescription": "Allows the app to access your address.",
|
||||||
|
"loginTailscaleTitle": "Continue with Tailscale",
|
||||||
|
"loginTailscaleDescription": "We detected that you are accessing Tinyauth from an authorized Tailscale device. Would you like to continue with your Tailscale connection?",
|
||||||
|
"loginTailscaleDeviceName": "Device name:",
|
||||||
|
"loginTailscaleSubmit": "Continue with Tailscale",
|
||||||
|
"loginTailscaleOtherMethod": "Login with another method",
|
||||||
|
"loginTailscaleSuccess": "Successfully authenticated with Tailscale.",
|
||||||
|
"loginTailscaleFail": "Failed to authenticate with Tailscale. Please try again or use another login method.",
|
||||||
|
"logoutTailscaleSubtitle": "You are currently logged in with Tailscale on your device <code>{{deviceName}}</code>. Click the button below to logout."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,11 @@ const iconMap: Record<string, React.ReactNode> = {
|
|||||||
|
|
||||||
export const LoginPage = () => {
|
export const LoginPage = () => {
|
||||||
const { auth, tailscale } = useUserContext();
|
const { auth, tailscale } = useUserContext();
|
||||||
const { ui, oauth, auth: cauth } = useAppContext();
|
const {
|
||||||
|
ui,
|
||||||
|
oauth,
|
||||||
|
auth: { providers },
|
||||||
|
} = useAppContext();
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -56,15 +60,15 @@ export const LoginPage = () => {
|
|||||||
const oidcParams = useOIDCParams(searchParams);
|
const oidcParams = useOIDCParams(searchParams);
|
||||||
|
|
||||||
const [isOauthAutoRedirect, setIsOauthAutoRedirect] = useState(
|
const [isOauthAutoRedirect, setIsOauthAutoRedirect] = useState(
|
||||||
cauth.providers.find((provider) => provider.id === oauth.autoRedirect) !==
|
providers.find((provider) => provider.id === oauth.autoRedirect) !==
|
||||||
undefined && redirectUri !== undefined,
|
undefined && redirectUri !== undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const oauthProviders = cauth.providers.filter(
|
const oauthProviders = providers.filter(
|
||||||
(provider) => provider.id !== "local" && provider.id !== "ldap",
|
(provider) => provider.id !== "local" && provider.id !== "ldap",
|
||||||
);
|
);
|
||||||
const userAuthConfigured =
|
const userAuthConfigured =
|
||||||
cauth.providers.find(
|
providers.find(
|
||||||
(provider) => provider.id === "local" || provider.id === "ldap",
|
(provider) => provider.id === "local" || provider.id === "ldap",
|
||||||
) !== undefined;
|
) !== undefined;
|
||||||
|
|
||||||
@@ -154,8 +158,8 @@ export const LoginPage = () => {
|
|||||||
mutationFn: () => axios.post("/api/user/tailscale"),
|
mutationFn: () => axios.post("/api/user/tailscale"),
|
||||||
mutationKey: ["tailscale"],
|
mutationKey: ["tailscale"],
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success("Logged in", {
|
toast.success(t("loginSuccessTitle"), {
|
||||||
description: t("Tailscale session confirmed"),
|
description: t("loginTailscaleSuccess"),
|
||||||
});
|
});
|
||||||
|
|
||||||
redirectTimer.current = window.setTimeout(() => {
|
redirectTimer.current = window.setTimeout(() => {
|
||||||
@@ -169,8 +173,8 @@ export const LoginPage = () => {
|
|||||||
}, 500);
|
}, 500);
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
toast.error("Failed to login", {
|
toast.error(t("loginFailTitle"), {
|
||||||
description: "Failed to authenticate with Tailscale.",
|
description: t("loginTailscaleFail"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -262,17 +266,15 @@ export const LoginPage = () => {
|
|||||||
<CardHeader className="gap-3">
|
<CardHeader className="gap-3">
|
||||||
<TailscaleIcon className="mx-auto h-8 w-8" />
|
<TailscaleIcon className="mx-auto h-8 w-8" />
|
||||||
<CardTitle className="text-center text-xl">
|
<CardTitle className="text-center text-xl">
|
||||||
Tinyauth · Tailscale
|
{t("loginTailscaleTitle")}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex flex-col gap-4">
|
<CardContent className="flex flex-col gap-4">
|
||||||
<div className="text-muted-foreground text-sm">
|
<div className="text-muted-foreground text-sm">
|
||||||
We detected that you are accessing Tinyauth from an authorized
|
{t("loginTailscaleDescription")}
|
||||||
Tailscale device. Would you like to continue with your Tailscale
|
|
||||||
credentials?
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-muted-foreground text-sm">
|
<div className="text-muted-foreground text-sm">
|
||||||
Machine Name: <code>{tailscale.nodeName}</code>
|
{t("loginTailscaleDeviceName")} <code>{tailscale.nodeName}</code>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="flex flex-col items-stretch gap-3">
|
<CardFooter className="flex flex-col items-stretch gap-3">
|
||||||
@@ -281,7 +283,7 @@ export const LoginPage = () => {
|
|||||||
onClick={() => tailscaleMutate()}
|
onClick={() => tailscaleMutate()}
|
||||||
loading={tailscaleIsPending}
|
loading={tailscaleIsPending}
|
||||||
>
|
>
|
||||||
Continue with Tailscale
|
{t("loginTailscaleSubmit")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="w-full"
|
className="w-full"
|
||||||
@@ -289,7 +291,7 @@ export const LoginPage = () => {
|
|||||||
onClick={() => setUseTailscale(false)}
|
onClick={() => setUseTailscale(false)}
|
||||||
disabled={tailscaleIsPending}
|
disabled={tailscaleIsPending}
|
||||||
>
|
>
|
||||||
Use other login method
|
{t("loginTailscaleOtherMethod")}
|
||||||
</Button>
|
</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -300,7 +302,7 @@ export const LoginPage = () => {
|
|||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="gap-1.5">
|
<CardHeader className="gap-1.5">
|
||||||
<CardTitle className="text-center text-xl">{ui.title}</CardTitle>
|
<CardTitle className="text-center text-xl">{ui.title}</CardTitle>
|
||||||
{cauth.providers.length > 0 && (
|
{providers.length > 0 && (
|
||||||
<CardDescription className="text-center">
|
<CardDescription className="text-center">
|
||||||
{oauthProviders.length !== 0
|
{oauthProviders.length !== 0
|
||||||
? t("loginTitle")
|
? t("loginTitle")
|
||||||
@@ -338,7 +340,7 @@ export const LoginPage = () => {
|
|||||||
})()}
|
})()}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{cauth.providers.length == 0 && (
|
{providers.length == 0 && (
|
||||||
<pre className="break-normal! text-sm text-red-600">
|
<pre className="break-normal! text-sm text-red-600">
|
||||||
{t("failedToFetchProvidersTitle")}
|
{t("failedToFetchProvidersTitle")}
|
||||||
</pre>
|
</pre>
|
||||||
|
|||||||
@@ -75,9 +75,17 @@ export const LogoutPage = () => {
|
|||||||
if (auth.providerId === "tailscale") {
|
if (auth.providerId === "tailscale") {
|
||||||
return (
|
return (
|
||||||
<LogoutLayout logoutMutation={logoutMutation}>
|
<LogoutLayout logoutMutation={logoutMutation}>
|
||||||
You are currently logged in with the Tailscale integration identified by
|
<Trans
|
||||||
the <code>{tailscale.nodeName}</code> node. Click the button below to
|
i18nKey="logoutTailscaleSubtitle"
|
||||||
log out.
|
t={t}
|
||||||
|
components={{
|
||||||
|
code: <code />,
|
||||||
|
}}
|
||||||
|
values={{
|
||||||
|
deviceName: tailscale.nodeName,
|
||||||
|
}}
|
||||||
|
shouldUnescape={true}
|
||||||
|
/>
|
||||||
</LogoutLayout>
|
</LogoutLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@@ -38,16 +37,17 @@ type Services struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BootstrapApp struct {
|
type BootstrapApp struct {
|
||||||
config model.Config
|
config model.Config
|
||||||
runtime model.RuntimeConfig
|
runtime model.RuntimeConfig
|
||||||
services Services
|
services Services
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
queries *repository.Queries
|
queries *repository.Queries
|
||||||
router *gin.Engine
|
router *gin.Engine
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
listeners []Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBootstrapApp(config model.Config) *BootstrapApp {
|
func NewBootstrapApp(config model.Config) *BootstrapApp {
|
||||||
@@ -254,56 +254,32 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
app.wg.Go(app.heartbeatRoutine)
|
app.wg.Go(app.heartbeatRoutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create err channel to listen for server errors
|
// setup listeners
|
||||||
errChanLen := 0
|
|
||||||
|
|
||||||
runUnix := app.config.Server.SocketPath != ""
|
runUnix := app.config.Server.SocketPath != ""
|
||||||
runHTTP := app.config.Server.SocketPath == "" || app.config.Server.ConcurrentListenersEnabled
|
runHTTP := app.config.Server.SocketPath == "" || app.config.Server.ConcurrentListenersEnabled
|
||||||
runTailscale := app.services.tailscaleService != nil
|
runTailscale := app.services.tailscaleService != nil
|
||||||
|
|
||||||
if runUnix {
|
if runHTTP {
|
||||||
errChanLen++
|
app.listeners = append(app.listeners, ListenerHTTP)
|
||||||
}
|
}
|
||||||
|
|
||||||
if runHTTP {
|
if runUnix {
|
||||||
errChanLen++
|
app.listeners = append(app.listeners, ListenerUnix)
|
||||||
}
|
}
|
||||||
|
|
||||||
if runTailscale {
|
if runTailscale {
|
||||||
errChanLen++
|
app.listeners = append(app.listeners, ListenerTailscale)
|
||||||
}
|
}
|
||||||
|
|
||||||
errChan := make(chan error, errChanLen)
|
|
||||||
|
|
||||||
if app.config.Server.ConcurrentListenersEnabled {
|
if app.config.Server.ConcurrentListenersEnabled {
|
||||||
app.log.App.Info().Msg("Concurrent listeners enabled, will run on all available listeners")
|
app.log.App.Info().Msg("Concurrent listeners enabled, will run on all available listeners")
|
||||||
}
|
}
|
||||||
|
|
||||||
// serve unix
|
// run listeners
|
||||||
if runUnix {
|
lec, err := app.runListeners()
|
||||||
app.wg.Go(func() {
|
|
||||||
if err := app.serveUnix(); err != nil {
|
|
||||||
errChan <- err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// serve to http
|
if err != nil {
|
||||||
if runHTTP {
|
return fmt.Errorf("failed to run listeners: %w", err)
|
||||||
app.wg.Go(func() {
|
|
||||||
if err := app.serveHTTP(); err != nil {
|
|
||||||
errChan <- err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// serve to tailscale
|
|
||||||
if runTailscale {
|
|
||||||
app.wg.Go(func() {
|
|
||||||
if err := app.serveTailscale(); err != nil {
|
|
||||||
errChan <- err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// monitor cancellation and server errors
|
// monitor cancellation and server errors
|
||||||
@@ -312,123 +288,14 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
case <-app.ctx.Done():
|
case <-app.ctx.Done():
|
||||||
app.log.App.Info().Msg("Oh, it's time for me to go, bye!")
|
app.log.App.Info().Msg("Oh, it's time for me to go, bye!")
|
||||||
return nil
|
return nil
|
||||||
case err := <-errChan:
|
case err := <-lec:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("server error: %w", err)
|
return fmt.Errorf("listener error: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *BootstrapApp) serveHTTP() error {
|
|
||||||
address := fmt.Sprintf("%s:%d", app.config.Server.Address, app.config.Server.Port)
|
|
||||||
|
|
||||||
app.log.App.Info().Msgf("Starting server on %s", address)
|
|
||||||
|
|
||||||
server := &http.Server{
|
|
||||||
Addr: address,
|
|
||||||
Handler: app.router.Handler(),
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-app.ctx.Done()
|
|
||||||
app.log.App.Debug().Msg("Shutting down http listener")
|
|
||||||
server.Shutdown(app.ctx)
|
|
||||||
}()
|
|
||||||
|
|
||||||
err := server.ListenAndServe()
|
|
||||||
|
|
||||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
return fmt.Errorf("failed to start http listener: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *BootstrapApp) serveUnix() error {
|
|
||||||
if app.config.Server.SocketPath == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := os.Stat(app.config.Server.SocketPath)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
app.log.App.Info().Msgf("Removing existing socket file %s", app.config.Server.SocketPath)
|
|
||||||
err := os.Remove(app.config.Server.SocketPath)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to remove existing socket file: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app.log.App.Info().Msgf("Starting server on unix socket %s", app.config.Server.SocketPath)
|
|
||||||
|
|
||||||
listener, err := net.Listen("unix", app.config.Server.SocketPath)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create unix socket listener: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
server := &http.Server{
|
|
||||||
Handler: app.router.Handler(),
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdown := func() {
|
|
||||||
server.Shutdown(app.ctx)
|
|
||||||
listener.Close()
|
|
||||||
os.Remove(app.config.Server.SocketPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-app.ctx.Done()
|
|
||||||
app.log.App.Debug().Msg("Shutting down unix socket listener")
|
|
||||||
shutdown()
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = server.Serve(listener)
|
|
||||||
|
|
||||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
shutdown()
|
|
||||||
return fmt.Errorf("failed to start unix socket listener: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *BootstrapApp) serveTailscale() error {
|
|
||||||
app.log.App.Info().Msgf("Starting Tailscale server on %s", app.services.tailscaleService.GetHostname())
|
|
||||||
|
|
||||||
listener, err := app.services.tailscaleService.CreateListener()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create tailscale listener: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
server := &http.Server{
|
|
||||||
Handler: app.router.Handler(),
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdown := func() {
|
|
||||||
server.Shutdown(app.ctx)
|
|
||||||
listener.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-app.ctx.Done()
|
|
||||||
app.log.App.Debug().Msg("Shutting down Tailscale listener")
|
|
||||||
shutdown()
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = server.Serve(listener)
|
|
||||||
|
|
||||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
shutdown()
|
|
||||||
return fmt.Errorf("failed to start tailscale listener: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *BootstrapApp) heartbeatRoutine() {
|
func (app *BootstrapApp) heartbeatRoutine() {
|
||||||
ticker := time.NewTicker(time.Duration(12) * time.Hour)
|
ticker := time.NewTicker(time.Duration(12) * time.Hour)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package bootstrap
|
package bootstrap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/controller"
|
"github.com/tinyauthapp/tinyauth/internal/controller"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/middleware"
|
"github.com/tinyauthapp/tinyauth/internal/middleware"
|
||||||
@@ -9,6 +13,14 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Listener int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ListenerHTTP Listener = iota
|
||||||
|
ListenerUnix
|
||||||
|
ListenerTailscale
|
||||||
|
)
|
||||||
|
|
||||||
func (app *BootstrapApp) setupRouter() error {
|
func (app *BootstrapApp) setupRouter() error {
|
||||||
// we don't want gin debug mode
|
// we don't want gin debug mode
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
@@ -53,3 +65,119 @@ func (app *BootstrapApp) setupRouter() error {
|
|||||||
app.router = engine
|
app.router = engine
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *BootstrapApp) runListeners() (chan error, error) {
|
||||||
|
// lec -> listener error channel
|
||||||
|
lec := make(chan error, len(app.listeners))
|
||||||
|
|
||||||
|
for _, listenerType := range app.listeners {
|
||||||
|
listenerFunc, err := app.listenerFromType(listenerType)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get listener function: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.wg.Go(func() {
|
||||||
|
lec <- listenerFunc()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return lec, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *BootstrapApp) listenerFromType(listenerType Listener) (func() error, error) {
|
||||||
|
switch listenerType {
|
||||||
|
case ListenerHTTP:
|
||||||
|
return app.serveHTTP, nil
|
||||||
|
case ListenerUnix:
|
||||||
|
return app.serveUnix, nil
|
||||||
|
case ListenerTailscale:
|
||||||
|
return app.serveTailscale, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid listener type: %d", listenerType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *BootstrapApp) serveHTTP() error {
|
||||||
|
address := fmt.Sprintf("%s:%d", app.config.Server.Address, app.config.Server.Port)
|
||||||
|
|
||||||
|
app.log.App.Info().Msgf("Starting server on %s", address)
|
||||||
|
|
||||||
|
listener, err := net.Listen("tcp", address)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create tcp listener: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: address,
|
||||||
|
Handler: app.router.Handler(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.serve(listener, server, "http")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *BootstrapApp) serveUnix() error {
|
||||||
|
_, err := os.Stat(app.config.Server.SocketPath)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
app.log.App.Info().Msgf("Removing existing socket file %s", app.config.Server.SocketPath)
|
||||||
|
err := os.Remove(app.config.Server.SocketPath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to remove existing socket file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.log.App.Info().Msgf("Starting server on unix socket %s", app.config.Server.SocketPath)
|
||||||
|
|
||||||
|
listener, err := net.Listen("unix", app.config.Server.SocketPath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create unix socket listener: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
server := &http.Server{
|
||||||
|
Handler: app.router.Handler(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.serve(listener, server, "unix socket")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *BootstrapApp) serveTailscale() error {
|
||||||
|
app.log.App.Info().Msgf("Starting Tailscale server on %s", fmt.Sprintf("https://%s", app.services.tailscaleService.GetHostname()))
|
||||||
|
|
||||||
|
listener, err := app.services.tailscaleService.CreateListener()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create tailscale listener: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
server := &http.Server{
|
||||||
|
Handler: app.router.Handler(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.serve(listener, server, "tailscale")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *BootstrapApp) serve(listener net.Listener, server *http.Server, name string) error {
|
||||||
|
shutdown := func() {
|
||||||
|
server.Shutdown(app.ctx)
|
||||||
|
listener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-app.ctx.Done()
|
||||||
|
app.log.App.Debug().Msgf("Shutting down %s listener", name)
|
||||||
|
shutdown()
|
||||||
|
}()
|
||||||
|
|
||||||
|
err := server.Serve(listener)
|
||||||
|
|
||||||
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
shutdown()
|
||||||
|
return fmt.Errorf("failed to start %s listener: %w", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func NewDefaultConfiguration() *Config {
|
|||||||
ConfigFile: "",
|
ConfigFile: "",
|
||||||
},
|
},
|
||||||
Tailscale: TailscaleConfig{
|
Tailscale: TailscaleConfig{
|
||||||
Dir: "./state",
|
Dir: "./tailscale_state",
|
||||||
},
|
},
|
||||||
LabelProvider: "auto",
|
LabelProvider: "auto",
|
||||||
}
|
}
|
||||||
@@ -206,6 +206,7 @@ type ExperimentalConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TailscaleConfig struct {
|
type TailscaleConfig struct {
|
||||||
|
Enabled bool `description:"Enable Tailscale integration." yaml:"enabled"`
|
||||||
Dir string `description:"Tailscale state directory." yaml:"dir"`
|
Dir string `description:"Tailscale state directory." yaml:"dir"`
|
||||||
Hostname string `description:"Tailscale hostname." yaml:"hostname"`
|
Hostname string `description:"Tailscale hostname." yaml:"hostname"`
|
||||||
AuthKey string `description:"Tailscale auth key." yaml:"authKey"`
|
AuthKey string `description:"Tailscale auth key." yaml:"authKey"`
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ type TailscaleService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewTailscaleService(log *logger.Logger, config model.Config, ctx context.Context, wg *sync.WaitGroup) (*TailscaleService, error) {
|
func NewTailscaleService(log *logger.Logger, config model.Config, ctx context.Context, wg *sync.WaitGroup) (*TailscaleService, error) {
|
||||||
|
if !config.Tailscale.Enabled {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
srv := new(tsnet.Server)
|
srv := new(tsnet.Server)
|
||||||
|
|
||||||
// node options
|
// node options
|
||||||
|
|||||||
Reference in New Issue
Block a user