diff --git a/config/config.go b/config/config.go
index df69d76..5f568c9 100644
--- a/config/config.go
+++ b/config/config.go
@@ -12,19 +12,18 @@ import (
// Config struct
type Config struct {
- AddrDev string
- AddrUser string
- AddrWeb string
- WebRedirURL string
- WebPort int
- SslCert string
- SslKey string
- SslCacert string // mTLS for device
- HTTPUsername string
- HTTPPassword string
- Token string
- FontSize int
- WhiteList map[string]bool
+ AddrDev string
+ AddrUser string
+ AddrWeb string
+ WebRedirURL string
+ WebPort int
+ SslCert string
+ SslKey string
+ SslCacert string // mTLS for device
+ Token string
+ FontSize int
+ WhiteList map[string]bool
+ DB string
}
func getConfigOpt(yamlCfg *yaml.File, name string, opt interface{}) {
@@ -44,16 +43,15 @@ func getConfigOpt(yamlCfg *yaml.File, name string, opt interface{}) {
// Parse config
func Parse(c *cli.Context) *Config {
cfg := &Config{
- AddrDev: c.String("addr-dev"),
- AddrUser: c.String("addr-user"),
- AddrWeb: c.String("addr-web"),
- WebRedirURL: c.String("web-redir-url"),
- SslCert: c.String("ssl-cert"),
- SslKey: c.String("ssl-key"),
- SslCacert: c.String("ssl-cacert"),
- HTTPUsername: c.String("http-username"),
- HTTPPassword: c.String("http-password"),
- Token: c.String("token"),
+ AddrDev: c.String("addr-dev"),
+ AddrUser: c.String("addr-user"),
+ AddrWeb: c.String("addr-web"),
+ WebRedirURL: c.String("web-redir-url"),
+ SslCert: c.String("ssl-cert"),
+ SslKey: c.String("ssl-key"),
+ SslCacert: c.String("ssl-cacert"),
+ Token: c.String("token"),
+ DB: c.String("db"),
}
cfg.WhiteList = make(map[string]bool)
@@ -77,9 +75,8 @@ func Parse(c *cli.Context) *Config {
getConfigOpt(yamlCfg, "ssl-cert", &cfg.SslCert)
getConfigOpt(yamlCfg, "ssl-key", &cfg.SslKey)
getConfigOpt(yamlCfg, "ssl-cacert", &cfg.SslCacert)
- getConfigOpt(yamlCfg, "http-username", &cfg.HTTPUsername)
- getConfigOpt(yamlCfg, "http-password", &cfg.HTTPPassword)
getConfigOpt(yamlCfg, "token", &cfg.Token)
+ getConfigOpt(yamlCfg, "db", &cfg.DB)
getConfigOpt(yamlCfg, "font-size", &cfg.FontSize)
val, err := yamlCfg.Get("white-list")
diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json
index 7dcff13..d115101 100644
--- a/frontend/src/locales/en.json
+++ b/frontend/src/locales/en.json
@@ -4,14 +4,20 @@
"Enter username...": "Enter username...",
"Password": "Password",
"Enter password...": "Enter password...",
- "Login": "Login",
+ "Sign in": "Sign in",
"Reset": "Reset",
"username is required": "username is required",
+ "password is required": "password is required",
"Signin Fail! username or password wrong.": "Signin Fail! username or password wrong.",
"Refresh List": "Refresh List",
"Please enter the filter key...": "Please enter the filter key...",
"Execute command": "Execute command",
"device-count": "Online Device: {count}",
+ "Sign out": "Sign out",
+ "Sign up": "Sign up",
+ "New to Rttys?": "New to Rttys?",
+ "Already have an account?":"Already have an account?",
+ "Sign up Fail.": "Sign up Fail.",
"No devices connected": "No devices connected",
"Device ID": "Device ID",
"Connected time": "Connected time",
diff --git a/frontend/src/locales/zh-CN.json b/frontend/src/locales/zh-CN.json
index 1abb772..87aab14 100644
--- a/frontend/src/locales/zh-CN.json
+++ b/frontend/src/locales/zh-CN.json
@@ -4,14 +4,20 @@
"Enter username...": "请输入用户名...",
"Password": "密码",
"Enter password...": "请输入密码",
- "Login": "登录",
+ "Sign in": "登录",
"Reset": "复位",
"username is required": "用户名为必填项",
+ "password is required": "密码为必填项",
"Signin Fail! username or password wrong.": "登录失败,用户名或密码错误",
"Refresh List": "刷新列表",
"Please enter the filter key...": "请输入关键字进行过滤...",
"Execute command": "执行命令",
"device-count": "在线设备数: {count}",
+ "Sign out": "退出",
+ "Sign up": "注册",
+ "New to Rttys?": "新用户?",
+ "Already have an account?":"已经有一个账号?",
+ "Sign up Fail.": "注册失败",
"No devices connected": "没有设备连接",
"Device ID": "设备ID",
"Connected time": "已连接时长",
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index 0e86a15..e154a6e 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -42,7 +42,7 @@ router.beforeEach((to, from, next) => {
return;
}
- if (to.path !== '/login' && !sessionStorage.getItem('rtty-sid')) {
+ if (to.path !== '/login' && !sessionStorage.getItem('rttys-sid')) {
router.push('/login');
return;
}
diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue
index bc5d56e..99d99bf 100644
--- a/frontend/src/views/Home.vue
+++ b/frontend/src/views/Home.vue
@@ -6,7 +6,18 @@
{{ $t('Already have an account?') }}{{ $t('Sign in') }}
+{{ $t('New to Rttys?') }}{{ $t('Sign up') }}
@@ -23,13 +25,16 @@ @Component export default class Login extends Vue { + signup = false; + formData = { username: '', password: '' }; ruleValidate = { - username: [{required: true, trigger: 'blur', message: ''}] + username: [{required: true, trigger: 'blur', message: ''}], + password: [{required: true, trigger: 'blur', message: ''}] }; handleSubmit() { @@ -39,12 +44,26 @@ username: this.formData.username, password: this.formData.password }; - this.axios.post('/signin', params).then(res => { - sessionStorage.setItem('rtty-sid', res.data); - this.$router.push('/'); - }).catch(() => { - this.$message.error(this.$t('Signin Fail! username or password wrong.').toString()); - }); + + if (this.signup) { + this.axios.post('/signup', params).then(() => { + this.reset(); + this.signup = false; + this.$router.push('/login'); + }).catch(() => { + this.reset(); + this.$message.error(this.$t('Sign up Fail.').toString()); + }); + } else { + this.axios.post('/signin', params).then(res => { + sessionStorage.setItem('rttys-sid', res.data.sid); + sessionStorage.setItem('rttys-username', res.data.username); + sessionStorage.setItem('rttys-admin', res.data.admin); + this.$router.push('/'); + }).catch(() => { + this.$message.error(this.$t('Signin Fail! username or password wrong.').toString()); + }); + } } }); } @@ -55,6 +74,12 @@ mounted() { this.ruleValidate['username'][0].message = this.$t('username is required').toString(); + this.ruleValidate['password'][0].message = this.$t('password is required').toString(); + } + + created() { + this.signup = this.$route.query.signup === '1'; + sessionStorage.removeItem('rttys-sid'); } } diff --git a/frontend/vue.config.js b/frontend/vue.config.js index f502c4f..14a4622 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -21,6 +21,9 @@ module.exports = { '/signin': { target: 'http://127.0.0.1:5913' }, + '/signup': { + target: 'http://127.0.0.1:5913' + }, '/cmd/*': { target: 'http://127.0.0.1:5913' }, diff --git a/go.mod b/go.mod index 949ba2c..30b3690 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/json-iterator/go v1.1.9 github.com/kylelemons/go-gypsy v0.0.0-20160905020020-08cad365cd28 github.com/mattn/go-colorable v0.1.4 + github.com/mattn/go-sqlite3 v1.14.6 github.com/rs/zerolog v1.18.0 github.com/urfave/cli/v2 v2.3.0 golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d diff --git a/go.sum b/go.sum index 9bd9713..7dc01c0 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= diff --git a/http.go b/http.go index 0aed53c..06f9c87 100644 --- a/http.go +++ b/http.go @@ -1,6 +1,7 @@ package main import ( + "database/sql" "embed" "net" "net/http" @@ -10,6 +11,7 @@ import ( "github.com/gin-gonic/gin" jsoniter "github.com/json-iterator/go" + _ "github.com/mattn/go-sqlite3" "github.com/rs/zerolog/log" "github.com/zhaojh329/rttys/cache" "github.com/zhaojh329/rttys/config" @@ -33,12 +35,22 @@ func allowOrigin(w http.ResponseWriter) { } func httpLogin(cfg *config.Config, creds *credentials) bool { - if cfg.HTTPUsername != creds.Username { + if creds.Username == "" || creds.Password == "" { return false } - if cfg.HTTPPassword != "" { - return cfg.HTTPPassword == creds.Password + db, err := sql.Open("sqlite3", cfg.DB) + if err != nil { + log.Error().Msg(err.Error()) + return false + } + defer db.Close() + + cnt := 0 + + db.QueryRow("SELECT COUNT(*) FROM account WHERE username = ? AND password = ?", creds.Username, creds.Password).Scan(&cnt) + if cnt == 0 { + return false } return true @@ -69,9 +81,22 @@ func httpAuth(c *gin.Context) bool { return true } +func initDb(cfg *config.Config) { + db, err := sql.Open("sqlite3", cfg.DB) + if err != nil { + log.Error().Msg(err.Error()) + return + } + defer db.Close() + + db.Exec("CREATE TABLE IF NOT EXISTS account(username TEXT PRIMARY KEY NOT NULL, password TEXT NOT NULL)") +} + func httpStart(br *broker) { cfg := br.cfg + initDb(cfg) + httpSessions = cache.New(30*time.Minute, 5*time.Second) gin.SetMode(gin.ReleaseMode) @@ -167,13 +192,50 @@ func httpStart(br *broker) { httpSessions.Set(sid, true, 0) c.SetCookie("sid", sid, 0, "", "", false, true) - c.String(http.StatusOK, sid) + + c.JSON(http.StatusOK, gin.H{ + "sid": sid, + "username": creds.Username, + }) return } c.Status(http.StatusForbidden) }) + r.POST("/signup", func(c *gin.Context) { + var creds credentials + + err := jsoniter.NewDecoder(c.Request.Body).Decode(&creds) + if err != nil { + c.Status(http.StatusBadRequest) + return + } + + db, err := sql.Open("sqlite3", cfg.DB) + if err != nil { + log.Error().Msg(err.Error()) + c.Status(http.StatusInternalServerError) + return + } + defer db.Close() + + _, err = db.Exec("INSERT INTO account values(?,?)", creds.Username, creds.Password) + if err != nil { + log.Error().Msg(err.Error()) + + if strings.Contains(err.Error(), "UNIQUE constraint failed") { + c.Status(http.StatusForbidden) + } else { + c.Status(http.StatusInternalServerError) + } + + return + } + + c.Status(http.StatusOK) + }) + r.NoRoute(func(c *gin.Context) { if !strings.HasPrefix(c.Request.URL.Path, "/frontend/dist/") { c.Request.URL.Path = "/frontend/dist" + c.Request.URL.Path diff --git a/main.go b/main.go index 1c16f29..9c9b006 100644 --- a/main.go +++ b/main.go @@ -18,11 +18,6 @@ func runRttys(c *cli.Context) { cfg := config.Parse(c) - if cfg.HTTPUsername == "" { - fmt.Println("You must configure the http username by commandline or config file") - os.Exit(1) - } - log.Info().Msg("Go Version: " + runtime.Version()) log.Info().Msgf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH) @@ -131,6 +126,11 @@ func main() { Value: "", Usage: "white list(device IDs separated by spaces or *)", }, + &cli.StringFlag{ + Name: "db", + Value: "rttys.db", + Usage: "sqlite3 database path", + }, }, Action: func(c *cli.Context) error { runRttys(c) diff --git a/rttys.conf b/rttys.conf index b474e28..3ffbeec 100644 --- a/rttys.conf +++ b/rttys.conf @@ -3,10 +3,6 @@ #addr-web: :5914 #web-redir-url: -# Auth for http -http-username: rttys -http-password: rttys - #ssl-cert: /etc/rttys/rttys.crt #ssl-key: /etc/rttys/rttys.key @@ -20,3 +16,6 @@ http-password: rttys # http://localhost:5913/connect/rtty1 #white-list: "*" #white-list: rtty1 rtty2 + +# sqlite3 database path +#db: /etc/rttys/rttys.db