mirror of
https://github.com/fumiama/paper-manager.git
synced 2026-06-09 18:30:23 +08:00
finish upload avatar
This commit is contained in:
@@ -1,26 +1,27 @@
|
||||
package file
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/fumiama/paper-manager/backend/global"
|
||||
"github.com/fumiama/paper-manager/backend/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Handler serves contents in global.FileFolder
|
||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
// FileHandler serves contents in global.FileFolder
|
||||
func FileHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.IsMethod("GET", w, r) {
|
||||
return
|
||||
}
|
||||
i := strings.LastIndex(r.URL.Path, "/")
|
||||
fn := r.URL.Path[i+1:]
|
||||
if r.URL.Path[0] != '/' {
|
||||
r.URL.Path = "/" + r.URL.Path
|
||||
}
|
||||
fn := r.URL.Path[6:]
|
||||
if fn == "" {
|
||||
http.Error(w, "400 Bad Request: empty path", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
name := global.FileFolder + fn
|
||||
logrus.Infoln("[file.Handler]\t serve", name)
|
||||
logrus.Infoln("[file.FileHandler] serve", name)
|
||||
http.ServeFile(w, r, name)
|
||||
}
|
||||
@@ -52,9 +52,12 @@ func getLoginSalt(username string) (*saltinfo, error) {
|
||||
return nil, errNoSuchUser
|
||||
}
|
||||
s, _ := loginstatus.Load(username)
|
||||
if s != loginStatusNo {
|
||||
if s == loginStatusYes {
|
||||
return nil, errInvalidLoginStatus
|
||||
}
|
||||
if s >= loginStatusFailLast {
|
||||
return nil, errTooManyFailedLogins
|
||||
}
|
||||
salt := loginsalts.Get(username)
|
||||
if salt.count != nil {
|
||||
if atomic.AddUintptr(salt.count, 1) >= maxSaltCount {
|
||||
|
||||
@@ -9,13 +9,16 @@ import (
|
||||
|
||||
// Handler serves all backend /api call
|
||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path[0] != '/' {
|
||||
r.URL.Path = "/" + r.URL.Path
|
||||
}
|
||||
if r.URL.Path == "/api/getLoginSalt" {
|
||||
if !utils.IsMethod("GET", w, r) {
|
||||
return
|
||||
}
|
||||
username := r.URL.Query().Get("username")
|
||||
if username == "" {
|
||||
http.Error(w, "400 Bad Request: empty username", http.StatusBadRequest)
|
||||
writeresult(w, codeError, nil, "empty username", typeError)
|
||||
return
|
||||
}
|
||||
salt, err := getLoginSalt(username)
|
||||
@@ -58,6 +61,18 @@ func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
writeresult(w, codeSuccess, r, messageOk, typeSuccess)
|
||||
return
|
||||
}
|
||||
if r.URL.Path == "/api/logout" {
|
||||
if !utils.IsMethod("GET", w, r) {
|
||||
return
|
||||
}
|
||||
err := logout(r.Header.Get("Authorization"))
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, nil, messageOk, typeSuccess)
|
||||
return
|
||||
}
|
||||
if !utils.IsMethod("GET", w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
98
backend/api/upload.go
Normal file
98
backend/api/upload.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fumiama/imgsz"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/paper-manager/backend/global"
|
||||
"github.com/fumiama/paper-manager/backend/utils"
|
||||
)
|
||||
|
||||
type upload struct {
|
||||
Message string `json:"message"`
|
||||
Code int `json:"code"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// UploadHandler receives uploaded files
|
||||
func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.IsMethod("POST", w, r) {
|
||||
return
|
||||
}
|
||||
token := r.Header.Get("Authorization")
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
ff, h, err := r.FormFile("avatar")
|
||||
if err == nil {
|
||||
defer ff.Close()
|
||||
ct := h.Header.Get("Content-Type")
|
||||
un := h.Filename
|
||||
logrus.Infoln("[file.UploadHandler] receive avatar, username:", un, "& mime:", ct)
|
||||
if !strings.HasPrefix(ct, "image/") {
|
||||
writeresult(w, codeError, nil, "invalid mimetype", typeError)
|
||||
return
|
||||
}
|
||||
if un != user.Name {
|
||||
writeresult(w, codeError, nil, "username mismatch", typeError)
|
||||
return
|
||||
}
|
||||
err = os.MkdirAll(global.FileFolder+strconv.Itoa(*user.ID), 0755)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
buf := bytes.NewBuffer(make([]byte, 0, h.Size))
|
||||
_, err := io.Copy(buf, ff)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
data := buf.Bytes()
|
||||
_, format, err := imgsz.DecodeSize(buf)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
userf := global.FileFolder + strconv.Itoa(*user.ID) + "/"
|
||||
err = os.MkdirAll(userf, 0755)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
avf := userf + "avatar." + format
|
||||
err = os.WriteFile(avf, data, 0644)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
err = global.UserDB.UpdateUserInfo(*user.ID, "", avf[6:], "")
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, &upload{
|
||||
Message: messageOk,
|
||||
Code: codeSuccess,
|
||||
URL: avf[6:],
|
||||
}, messageOk, typeSuccess)
|
||||
user.Avtr = avf[6:]
|
||||
usertokens.Set(token, user)
|
||||
logrus.Infoln("[file.UploadHandler] save avatar to", avf[6:])
|
||||
return
|
||||
}
|
||||
if err != http.ErrMissingFile {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,10 +2,16 @@ package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/paper-manager/backend/global"
|
||||
)
|
||||
|
||||
const (
|
||||
chineseDateLayout = "2006年01月02日15时04分05秒"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidToken = errors.New("invalid token")
|
||||
)
|
||||
@@ -18,6 +24,9 @@ type getUserInfoResult struct {
|
||||
Desc string `json:"desc"`
|
||||
HomePath string `json:"homePath"`
|
||||
Roles []role `json:"roles"`
|
||||
Date string `json:"date"`
|
||||
Last string `json:"last"`
|
||||
Contact string `json:"contact"`
|
||||
}
|
||||
|
||||
func getUserInfo(token string) (*getUserInfoResult, error) {
|
||||
@@ -25,6 +34,16 @@ func getUserInfo(token string) (*getUserInfoResult, error) {
|
||||
if user == nil {
|
||||
return nil, errInvalidToken
|
||||
}
|
||||
cont := user.Cont
|
||||
if len(cont) > 7 {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString(cont[:3])
|
||||
for i := 0; i < len(cont)-7; i++ {
|
||||
sb.WriteByte('*')
|
||||
}
|
||||
sb.WriteString(cont[len(cont)-4:])
|
||||
cont = sb.String()
|
||||
}
|
||||
return &getUserInfoResult{
|
||||
UserID: *user.ID,
|
||||
Username: user.Name,
|
||||
@@ -37,6 +56,19 @@ func getUserInfo(token string) (*getUserInfoResult, error) {
|
||||
}
|
||||
return "/dashboard/workbench"
|
||||
}(),
|
||||
Roles: []role{{RoleName: user.Role.Nick(), Value: user.Role.String()}},
|
||||
Roles: []role{{RoleName: user.Role.Nick(), Value: user.Role.String()}},
|
||||
Date: time.Unix(user.Date, 0).Format(chineseDateLayout),
|
||||
Last: time.Unix(user.Last, 0).Format(chineseDateLayout),
|
||||
Contact: cont,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func logout(token string) error {
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
return errInvalidToken
|
||||
}
|
||||
loginstatus.Delete(user.Name)
|
||||
usertokens.Delete(token)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func init() {
|
||||
func initdir(folder string) {
|
||||
err := os.MkdirAll(folder, 0755)
|
||||
if err != nil {
|
||||
logrus.Errorln("[os.MkdirAll]\t", err)
|
||||
logrus.Errorln("[os.MkdirAll]", err)
|
||||
os.Exit(line())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ var (
|
||||
ErrEmptyUserID = errors.New("empty user ID")
|
||||
ErrEmptyContect = errors.New("empty contact")
|
||||
ErrUsernameExists = errors.New("username exists")
|
||||
ErrInvalidName = errors.New("invalid name")
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -88,7 +89,7 @@ func init() {
|
||||
Cont: "028-61830156",
|
||||
Desc: "日は山の端にかかりぬ",
|
||||
}, "系统")
|
||||
logrus.Warn("[global.user] 初次启动, 创建初始账户 fumiama 密码 123456")
|
||||
logrus.Warn("[user] 初次启动, 创建初始账户 fumiama 密码 123456")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +122,11 @@ func (u *UserDatabase) AddUser(user *User, opname string) error {
|
||||
if u.IsNameExists(user.Name) {
|
||||
return ErrUsernameExists
|
||||
}
|
||||
for _, c := range user.Name {
|
||||
if !(c >= '0' && c <= '9') && !(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') {
|
||||
return ErrInvalidName
|
||||
}
|
||||
}
|
||||
user.Date = time.Now().Unix()
|
||||
user.Last = user.Date
|
||||
_ = u.notifyUserAdded(opname, user.Name)
|
||||
@@ -146,7 +152,7 @@ func (u *UserDatabase) UpdateUserInfo(id int, nick, avtr, desc string) error {
|
||||
}
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
return u.db.Insert(UserTableUser, user)
|
||||
return u.db.Insert(UserTableUser, &user)
|
||||
}
|
||||
|
||||
// UpdateUserRole ...
|
||||
@@ -161,7 +167,7 @@ func (u *UserDatabase) UpdateUserRole(id int, nr UserRole) error {
|
||||
user.Role = nr
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
return u.db.Insert(UserTableUser, user)
|
||||
return u.db.Insert(UserTableUser, &user)
|
||||
}
|
||||
|
||||
// UpdateUserPassword ...
|
||||
@@ -178,7 +184,7 @@ func (u *UserDatabase) UpdateUserPassword(id int, npwd string) error {
|
||||
_ = u.notifyPasswordChange(user.Name, npwd)
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
return u.db.Insert(UserTableUser, user)
|
||||
return u.db.Insert(UserTableUser, &user)
|
||||
}
|
||||
|
||||
// UpdateUserContact ...
|
||||
@@ -194,12 +200,18 @@ func (u *UserDatabase) UpdateUserContact(id int, ncont string) error {
|
||||
_ = u.notifyContactChange(user.Name, ncont)
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
return u.db.Insert(UserTableUser, user)
|
||||
return u.db.Insert(UserTableUser, &user)
|
||||
}
|
||||
|
||||
// GetUserByName avoids sql injection by removing ; ' " =
|
||||
func (u *UserDatabase) GetUserByName(username string) (user User, err error) {
|
||||
username = strings.NewReplacer(";", "", "'", "", `"`, "", "=", "").Replace(username)
|
||||
for _, c := range username {
|
||||
if !(c >= '0' && c <= '9') && !(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') {
|
||||
err = ErrInvalidName
|
||||
return
|
||||
}
|
||||
}
|
||||
u.mu.RLock()
|
||||
err = u.db.Find(UserTableUser, &user, "WHERE Name='"+username+"'")
|
||||
u.mu.RUnlock()
|
||||
@@ -209,6 +221,11 @@ func (u *UserDatabase) GetUserByName(username string) (user User, err error) {
|
||||
// IsNameExists avoids sql injection by removing ; ' " =
|
||||
func (u *UserDatabase) IsNameExists(username string) bool {
|
||||
username = strings.NewReplacer(";", "", "'", "", `"`, "", "=", "").Replace(username)
|
||||
for _, c := range username {
|
||||
if !(c >= '0' && c <= '9') && !(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
u.mu.RLock()
|
||||
defer u.mu.RUnlock()
|
||||
return u.db.CanFind(UserTableUser, "WHERE Name='"+username+"'")
|
||||
@@ -265,6 +282,21 @@ func (u *UserDatabase) GetSuperIDs() (ids []int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// IsUser checks if token is valid for a user
|
||||
func (user *User) IsUser() bool {
|
||||
return user.Role == RoleUser || user.Role == RoleFileManager || user.Role == RoleSuper
|
||||
}
|
||||
|
||||
// IsFileManager checks if token is valid for a filemgr
|
||||
func (user *User) IsFileManager() bool {
|
||||
return user.Role == RoleFileManager || user.Role == RoleSuper
|
||||
}
|
||||
|
||||
// IsSuper checks if token is valid for a super
|
||||
func (user *User) IsSuper() bool {
|
||||
return user.Role == RoleSuper
|
||||
}
|
||||
|
||||
// Message is shown in the workbench
|
||||
type Message struct {
|
||||
ID *int
|
||||
@@ -282,7 +314,7 @@ func (u *UserDatabase) SendMessage(m *Message) error {
|
||||
m.Date = time.Now().Unix()
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
return u.db.InsertUnique(UserTableMessage, &m)
|
||||
return u.db.InsertUnique(UserTableMessage, m)
|
||||
}
|
||||
|
||||
// NotifyRegister will send register notification to all supers
|
||||
@@ -290,6 +322,11 @@ func (u *UserDatabase) NotifyRegister(name, cont, pswd string) error {
|
||||
if name == "" {
|
||||
return ErrEmptyName
|
||||
}
|
||||
for _, c := range name {
|
||||
if !(c >= '0' && c <= '9') && !(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') {
|
||||
return ErrInvalidName
|
||||
}
|
||||
}
|
||||
if pswd == "" {
|
||||
return ErrEmptyPassword
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func IP(r *http.Request) string {
|
||||
// IsMethod check if the method meets the requirement
|
||||
// and response 405 Method Not Allowed if not matched
|
||||
func IsMethod(m string, w http.ResponseWriter, r *http.Request) bool {
|
||||
logrus.Infoln("[utils.IsMethod]\t accept", IP(r), r.Method, r.URL)
|
||||
logrus.Infoln("[utils.IsMethod] accept", IP(r), r.Method, r.URL)
|
||||
if r.Method != m {
|
||||
http.Error(w, "405 Method Not Allowed", http.StatusMethodNotAllowed)
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user