♻️ Refactor logger in auth

This commit is contained in:
OpenWaygate committed 2025-09-03 00:37:02 +08:00
1 parent abe4f996f9
commit 20a6544d89
2 files changed
+77 -57

No files matched your search

+62 -42
View File
@@ -3,9 +3,8 @@ package auth
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"log/slog"
"net"
"net/http"
"net/url"
@@ -20,6 +19,17 @@ import (
)
const (
createSvcFailed = "failed to create YouTube service"
parseUrlFailed = "failed to parse redirect URL"
stateMatchFailed = "state doesn't match, possible CSRF attack"
readPromptFailed = "failed to read prompt"
exchangeFailed = "failed to exchange token"
listenFailed = "failed to start web server"
cacheTokenFailed = "failed to cache token"
parseTokenFailed = "failed to parse token"
refreshTokenFailed = "failed to refresh token, please re-authenticate in cli"
parseSecretFailed = "failed to parse client secret"
cacheTokenFile = "youtube.token.json"
credentialFile = "client_secret.json"
manualInputHint = `
@@ -33,16 +43,7 @@ ONLY 'COPY-THIS' part is the code you need to enter on command line.
)
var (
state = utils.RandomStage()
errStateMismatch = errors.New("state doesn't match, possible CSRF attack")
errReadPrompt = errors.New("unable to read prompt")
errExchange = errors.New("unable retrieve token from web or prompt")
errStartWeb = errors.New("unable to start web server")
errCacheToken = errors.New("unable to cache token")
errParseToken = errors.New("unable to parse token")
errRefreshToken = errors.New("unable to refresh token, please re-authenticate in cli")
errParseSecret = errors.New("unable to parse client secret")
state = utils.RandomStage()
scope = []string{
youtube.YoutubeScope,
youtube.YoutubeForceSslScope,
@@ -54,7 +55,8 @@ func (s *svc) GetService() *youtube.Service {
client := s.refreshClient()
service, err := youtube.NewService(s.ctx, option.WithHTTPClient(client))
if err != nil {
log.Fatalln(errors.Join(errCreateSvc, err))
slog.Error(createSvcFailed, "error", err)
os.Exit(1)
}
s.service = service
@@ -81,7 +83,8 @@ func (s *svc) refreshClient() (client *http.Client) {
s.saveToken(cacheTokenFile, authedToken)
return client
} else if err != nil {
log.Fatalln(errors.Join(errRefreshToken, err))
slog.Error(refreshTokenFailed, "error", err)
os.Exit(1)
}
if authedToken != nil && s.Cacheable {
@@ -106,7 +109,8 @@ func (s *svc) newClient(config *oauth2.Config) (
func (s *svc) getConfig() *oauth2.Config {
config, err := google.ConfigFromJSON([]byte(s.Credential), scope...)
if err != nil {
log.Fatalln(errors.Join(errParseSecret, err))
slog.Error(parseSecretFailed, "error", err)
os.Exit(1)
}
return config
@@ -115,35 +119,45 @@ func (s *svc) getConfig() *oauth2.Config {
func (s *svc) startWebServer(redirectURL string) chan string {
u, err := url.Parse(redirectURL)
if err != nil {
log.Fatalln(errors.Join(errStartWeb, err))
slog.Error(parseUrlFailed, "url", redirectURL, "error", err)
os.Exit(1)
}
listener, err := net.Listen("tcp", u.Host)
if err != nil {
log.Fatalln(errors.Join(errStartWeb, err))
slog.Error(listenFailed, "host", u.Host, "error", err)
os.Exit(1)
}
codeCh := make(chan string)
go http.Serve(
listener, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
return
}
s := r.FormValue("state")
if s != state {
log.Fatalf("%v: %s != %s\n", errStateMismatch, s, state)
}
code := r.FormValue("code")
codeCh <- code
_ = listener.Close()
w.Header().Set("Content-Type", "text/plain")
_, _ = fmt.Fprintf(
w, "Received code: %s\r\nYou can now safely close this window.", code,
)
},
),
)
go func() {
_ = http.Serve(
listener, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
return
}
s := r.FormValue("state")
if s != state {
slog.Error(
stateMatchFailed,
"actual", s,
"expected", state,
)
os.Exit(1)
}
code := r.FormValue("code")
codeCh <- code
_ = listener.Close()
w.Header().Set("Content-Type", "text/plain")
_, _ = fmt.Fprintf(
w, "Received code: %s\r\nYou can now safely close this window.",
code,
)
},
),
)
}()
return codeCh
}
@@ -156,7 +170,8 @@ func (s *svc) getCodeFromPrompt(authURL string) (code string) {
fmt.Print(manualInputHint)
_, err := fmt.Scan(&code)
if err != nil {
log.Fatalln(errors.Join(errReadPrompt, err))
slog.Error(readPromptFailed, "error", err)
os.Exit(1)
}
if strings.HasPrefix(code, "4%2F") {
@@ -187,7 +202,8 @@ func (s *svc) getTokenFromWeb(
fmt.Printf("Authorization code generated: %s\n", code)
token, err := config.Exchange(context.TODO(), code)
if err != nil {
log.Fatalln(errors.Join(errExchange, err))
slog.Error(exchangeFailed, "error", err)
os.Exit(1)
}
return token
@@ -196,12 +212,16 @@ func (s *svc) getTokenFromWeb(
func (s *svc) saveToken(file string, token *oauth2.Token) {
f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Fatalln(errors.Join(errCacheToken, err))
slog.Error(cacheTokenFailed, "file", file, "error", err)
os.Exit(1)
}
defer f.Close()
defer func() {
_ = f.Close()
}()
err = json.NewEncoder(f).Encode(token)
if err != nil {
log.Fatalln(errors.Join(errCacheToken, err))
slog.Error(cacheTokenFailed, "error", err)
os.Exit(1)
}
}
+15 -15
View File
@@ -3,10 +3,8 @@ package auth
import (
"context"
"encoding/base64"
"errors"
"fmt"
"io/fs"
"log"
"log/slog"
"net/http"
"os"
"path/filepath"
@@ -17,14 +15,12 @@ import (
"google.golang.org/api/youtube/v3"
)
var (
errCreateSvc = errors.New("unable to create YouTube service")
errReadToken = errors.New("unable to read token")
errReadSecret = errors.New("unable to read client secret")
const (
readTokenFailed = "failed to read token"
readSecretFailed = "failed to read client secret"
authHint = "Please configure client secret as described in https://github.com/eat-pray-ai/yutu#prerequisites"
)
const missingClientSecretsHint string = "Please configure client secrets as described in https://github.com/eat-pray-ai/yutu#prerequisites"
type svc struct {
Cacheable bool `yaml:"cacheable" json:"cacheable"`
Credential string `yaml:"credential" json:"credential"`
@@ -75,8 +71,10 @@ func WithCredential(cred string, fsys fs.FS) Option {
// credBytes, err := yfs.ReadFile(cred)
credBytes, err := fs.ReadFile(fsys, relCred)
if err != nil {
fmt.Println(missingClientSecretsHint)
log.Fatalln(errors.Join(errReadSecret, err))
slog.Error(
readSecretFailed, "hint", authHint, "path", absCred, "error", err,
)
os.Exit(1)
}
s.Credential = string(credBytes)
return
@@ -87,8 +85,8 @@ func WithCredential(cred string, fsys fs.FS) Option {
} else if utils.IsJson(cred) {
s.Credential = cred
} else {
fmt.Println(missingClientSecretsHint)
log.Fatalln(errors.Join(errReadSecret, err))
slog.Error(parseSecretFailed, "hint", authHint, "error", err)
os.Exit(1)
}
}
}
@@ -112,7 +110,8 @@ func WithCacheToken(token string, fsys fs.FS) Option {
if _, err := fs.Stat(fsys, relToken); err == nil {
tokenBytes, err := fs.ReadFile(fsys, relToken)
if err != nil {
log.Fatalln(errors.Join(errReadToken, err))
slog.Error(readTokenFailed, "path", absToken, "error", err)
os.Exit(1)
}
s.CacheToken = string(tokenBytes)
s.Cacheable = true
@@ -128,7 +127,8 @@ func WithCacheToken(token string, fsys fs.FS) Option {
} else if utils.IsJson(token) {
s.CacheToken = token
} else {
log.Fatalln(errors.Join(errReadToken, err))
slog.Error(parseTokenFailed, "error", err)
os.Exit(1)
}
}
}