Skip to content

Commit

Permalink
feat: support user sign up
Browse files Browse the repository at this point in the history
Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
  • Loading branch information
zhaojh329 committed Feb 27, 2021
1 parent 4babe35 commit dd3fb3e
Show file tree
Hide file tree
Showing 12 changed files with 171 additions and 50 deletions.
47 changes: 22 additions & 25 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}) {
Expand All @@ -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)
Expand All @@ -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")
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "已连接时长",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
22 changes: 21 additions & 1 deletion frontend/src/views/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,18 @@
<el-button style="margin-right: 4px;" @click="showCmdForm" type="primary" :disabled="cmdStatus.execing > 0">
{{$t('Execute command')}}
</el-button>
<div style="float: right; color: #3399ff; font-size: 16px">{{ $t('device-count', {count: devlists.length}) }}</div>
<div style="float: right; margin-right: 10px">
<span style="margin-right: 20px; color: #3399ff; font-size: 24px">{{ $t('device-count', {count: devlists.length}) }}</span>
<el-dropdown @command="handleUserCommand">
<span class="el-dropdown-link">
<span style="color: #3399ff; font-size: 24px">{{ username }}</span>
<i class="el-icon-arrow-down el-icon--right" style="color: #3399ff; font-size: 24px"/>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="logout">{{ $t('Sign out') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-table v-loading="loading" :data="filtered"
style="margin-top: 10px; width: 100%" :empty-text="$t('No devices connected')"
@selection-change='handleSelection'>
Expand Down Expand Up @@ -160,6 +171,7 @@
}
})
export default class Home extends Vue {
username = '';
filterString = '';
loading = true;
devlists = [];
Expand Down Expand Up @@ -207,6 +219,12 @@
return '';
}
handleUserCommand(command: string) {
if (command === 'logout') {
this.$router.push('/login');
}
}
handleSearch() {
this.filtered = this.devlists.filter((d: DeviceInfo) => {
const filterString = this.filterString.toLowerCase();
Expand Down Expand Up @@ -365,6 +383,8 @@
}
mounted() {
this.username = sessionStorage.getItem('rttys-username') || '';
this.getDevices();
}
}
Expand Down
41 changes: 33 additions & 8 deletions frontend/src/views/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
show-password @keyup.enter.native="handleSubmit"/>
</el-form-item>
<el-form-item>
<el-button type="primary" style="width: 70%" @click="handleSubmit">{{ $t('Login') }}</el-button>
<el-button type="primary" style="width: 70%" @click="handleSubmit">{{ signup ? $t('Sign up') : $t('Sign in') }}</el-button>
<el-button type="warning" @click="reset">{{ $t('Reset') }}</el-button>
</el-form-item>
</el-form>
<p v-if="signup" style="text-align: center">{{ $t('Already have an account?') }}<a href="/login" style="text-decoration: none; color: #1c7cd6">{{ $t('Sign in') }}</a></p>
<p v-else style="text-align: center">{{ $t('New to Rttys?') }}<a href="/login?signup=1" style="text-decoration: none; color: #1c7cd6">{{ $t('Sign up') }}</a></p>
</el-card>
</template>

Expand All @@ -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() {
Expand All @@ -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());
});
}
}
});
}
Expand All @@ -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');
}
}
</script>
Expand Down
3 changes: 3 additions & 0 deletions frontend/vue.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
},
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
70 changes: 66 additions & 4 deletions http.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"database/sql"
"embed"
"net"
"net/http"
Expand All @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit dd3fb3e

Please sign in to comment.