mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-04-29 14:57:48 +08:00
Add logout (#13796)
### What problem does this PR solve? Add command: logout ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
@ -43,7 +43,6 @@ func (r *Router) Setup(engine *gin.Engine) {
|
||||
// Public routes
|
||||
admin.GET("/ping", r.handler.Ping)
|
||||
admin.POST("/login", r.handler.Login)
|
||||
admin.GET("/logout", r.handler.Logout)
|
||||
|
||||
admin.POST("/reports", r.handler.Reports)
|
||||
|
||||
@ -51,6 +50,8 @@ func (r *Router) Setup(engine *gin.Engine) {
|
||||
protected := admin.Group("")
|
||||
protected.Use(r.handler.AuthMiddleware())
|
||||
{
|
||||
|
||||
protected.GET("/logout", r.handler.Logout)
|
||||
// Auth
|
||||
protected.GET("/auth", r.handler.AuthCheck)
|
||||
|
||||
|
||||
@ -27,6 +27,16 @@ func (p *Parser) parseAdminLoginUser() (*Command, error) {
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseAdminLogout() (*Command, error) {
|
||||
cmd := NewCommand("logout")
|
||||
p.nextToken()
|
||||
// Semicolon is optional for UNSET TOKEN
|
||||
if p.curToken.Type == TokenSemicolon {
|
||||
p.nextToken()
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseAdminPingServer() (*Command, error) {
|
||||
cmd := NewCommand("ping")
|
||||
p.nextToken()
|
||||
|
||||
@ -110,7 +110,7 @@ func parseHostPort(hostPort string) (string, int, error) {
|
||||
func ParseConnectionArgs(args []string) (*ConnectionArgs, error) {
|
||||
// First, scan args to check for help, config file, and admin mode
|
||||
var configFilePath string
|
||||
|
||||
var adminMode bool = false
|
||||
for i := 0; i < len(args); i++ {
|
||||
arg := args[i]
|
||||
if arg == "--help" || arg == "-help" {
|
||||
@ -118,6 +118,8 @@ func ParseConnectionArgs(args []string) (*ConnectionArgs, error) {
|
||||
} else if (arg == "-f" || arg == "--config") && i+1 < len(args) {
|
||||
configFilePath = args[i+1]
|
||||
i++
|
||||
} else if arg == "--admin" {
|
||||
adminMode = true
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,45 +127,48 @@ func ParseConnectionArgs(args []string) (*ConnectionArgs, error) {
|
||||
var config *ConfigFile
|
||||
var err error
|
||||
|
||||
if configFilePath != "" {
|
||||
// User specified config file via -f
|
||||
config, err = LoadConfigFileFromPath(configFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Try default rf.yml
|
||||
config, err = LoadDefaultConfigFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Parse arguments manually to support both short and long forms
|
||||
// and to handle priority: command line > config file > defaults
|
||||
|
||||
// Build result from config file first (if exists), then override with command line flags
|
||||
result := &ConnectionArgs{}
|
||||
|
||||
if !adminMode {
|
||||
// Only user mode read config file
|
||||
if configFilePath != "" {
|
||||
// User specified config file via -f
|
||||
config, err = LoadConfigFileFromPath(configFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Try default rf.yml
|
||||
config, err = LoadDefaultConfigFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Apply config file values first (lower priority)
|
||||
if config != nil {
|
||||
// Parse host:port from config file
|
||||
if config.Host != "" {
|
||||
h, port, err := parseHostPort(config.Host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid host in config file: %v", err)
|
||||
}
|
||||
result.Host = h
|
||||
result.Port = port
|
||||
}
|
||||
result.UserName = config.UserName
|
||||
result.Password = config.Password
|
||||
result.APIToken = config.APIToken
|
||||
}
|
||||
}
|
||||
|
||||
// Get non-flag arguments (command to execute)
|
||||
var nonFlagArgs []string
|
||||
|
||||
// Apply config file values first (lower priority)
|
||||
if config != nil {
|
||||
// Parse host:port from config file
|
||||
if config.Host != "" {
|
||||
h, port, err := parseHostPort(config.Host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid host in config file: %v", err)
|
||||
}
|
||||
result.Host = h
|
||||
result.Port = port
|
||||
}
|
||||
result.UserName = config.UserName
|
||||
result.Password = config.Password
|
||||
result.APIToken = config.APIToken
|
||||
}
|
||||
|
||||
// Override with command line flags (higher priority)
|
||||
// Handle both short and long forms manually
|
||||
for i := 0; i < len(args); i++ {
|
||||
|
||||
@ -234,6 +234,35 @@ func (c *RAGFlowClient) loginUser(email, password string) (string, error) {
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (c *RAGFlowClient) Logout() (ResponseIf, error) {
|
||||
if c.HTTPClient.LoginToken == "" {
|
||||
return nil, fmt.Errorf("not logged in")
|
||||
}
|
||||
|
||||
var path string
|
||||
if c.ServerType == "admin" {
|
||||
path = "/admin/logout"
|
||||
} else {
|
||||
path = "/user/logout"
|
||||
}
|
||||
|
||||
resp, err := c.HTTPClient.Request("GET", path, c.ServerType == "admin", "web", nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result SimpleResponse
|
||||
if err = json.Unmarshal(resp.Body, &result); err != nil {
|
||||
return nil, fmt.Errorf("login failed: invalid JSON (%w)", err)
|
||||
}
|
||||
|
||||
if result.Code != 0 {
|
||||
return nil, fmt.Errorf("login failed: %s", result.Message)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// readPassword reads password from terminal without echoing
|
||||
func readPassword() (string, error) {
|
||||
// Check if stdin is a terminal by trying to get terminal size
|
||||
@ -301,6 +330,8 @@ func (c *RAGFlowClient) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) {
|
||||
switch cmd.Type {
|
||||
case "login_user":
|
||||
return nil, c.LoginUser(cmd)
|
||||
case "logout":
|
||||
return c.Logout()
|
||||
case "ping":
|
||||
return c.PingAdmin(cmd)
|
||||
case "benchmark":
|
||||
@ -350,6 +381,8 @@ func (c *RAGFlowClient) ExecuteUserCommand(cmd *Command) (ResponseIf, error) {
|
||||
return c.RegisterUser(cmd)
|
||||
case "login_user":
|
||||
return nil, c.LoginUser(cmd)
|
||||
case "logout":
|
||||
return c.Logout()
|
||||
case "ping":
|
||||
return c.PingServer(cmd)
|
||||
case "benchmark":
|
||||
|
||||
@ -149,6 +149,8 @@ func (l *Lexer) lookupIdent(ident string) Token {
|
||||
switch upper {
|
||||
case "LOGIN":
|
||||
return Token{Type: TokenLogin, Value: ident}
|
||||
case "LOGOUT":
|
||||
return Token{Type: TokenLogout, Value: ident}
|
||||
case "REGISTER":
|
||||
return Token{Type: TokenRegister, Value: ident}
|
||||
case "LIST":
|
||||
|
||||
@ -81,6 +81,8 @@ func (p *Parser) parseAdminCommand() (*Command, error) {
|
||||
switch p.curToken.Type {
|
||||
case TokenLogin:
|
||||
return p.parseAdminLoginUser()
|
||||
case TokenLogout:
|
||||
return p.parseAdminLogout()
|
||||
case TokenPing:
|
||||
return p.parseAdminPingServer()
|
||||
case TokenList:
|
||||
@ -131,6 +133,8 @@ func (p *Parser) parseUserCommand() (*Command, error) {
|
||||
switch p.curToken.Type {
|
||||
case TokenLogin:
|
||||
return p.parseLoginUser()
|
||||
case TokenLogout:
|
||||
return p.parseLogout()
|
||||
case TokenPing:
|
||||
return p.parsePingServer()
|
||||
case TokenList:
|
||||
|
||||
@ -26,6 +26,7 @@ type Command struct {
|
||||
const (
|
||||
// Keywords
|
||||
TokenLogin = iota
|
||||
TokenLogout
|
||||
TokenRegister
|
||||
TokenList
|
||||
TokenServices
|
||||
|
||||
@ -3,6 +3,16 @@ package cli
|
||||
import "fmt"
|
||||
|
||||
// Command parsers
|
||||
func (p *Parser) parseLogout() (*Command, error) {
|
||||
cmd := NewCommand("logout")
|
||||
p.nextToken()
|
||||
// Semicolon is optional for UNSET TOKEN
|
||||
if p.curToken.Type == TokenSemicolon {
|
||||
p.nextToken()
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseLoginUser() (*Command, error) {
|
||||
cmd := NewCommand("login_user")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user