파일 업로드/삭제, maintenance 메뉴 추가
This commit is contained in:
129
core/api.go
129
core/api.go
@ -2,6 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
@ -174,14 +175,40 @@ func (caller apiCaller) isValidUser(service any, category string) (valid bool, a
|
||||
}
|
||||
|
||||
func (caller apiCaller) filesAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
serviceid := r.FormValue("service")
|
||||
if len(serviceid) == 0 {
|
||||
serviceid = "000000000000"
|
||||
if r.Method == "GET" {
|
||||
hasAuth := caller.isGlobalAdmin()
|
||||
var email string
|
||||
if !*noauth {
|
||||
v, ok := caller.userinfo["email"]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
email = v.(string)
|
||||
_, hasAuth = caller.admins[email]
|
||||
}
|
||||
|
||||
servicename := r.FormValue("service")
|
||||
sh := caller.mg.services.get(servicename)
|
||||
if sh == nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !hasAuth {
|
||||
if hasAuth = sh.isValidAPIUser("maintenance", email); !hasAuth {
|
||||
hasAuth = sh.isValidAPIUser("service", email)
|
||||
}
|
||||
}
|
||||
|
||||
if !hasAuth {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return nil
|
||||
}
|
||||
|
||||
var files []fileDocumentDesc
|
||||
err := caller.mg.mongoClient.FindAllAs(CollectionFile, bson.M{
|
||||
"service": serviceid,
|
||||
"service": servicename,
|
||||
}, &files, options.Find().SetProjection(bson.M{
|
||||
"contents": 0,
|
||||
}))
|
||||
@ -189,17 +216,39 @@ func (caller apiCaller) filesAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(files) > 0 {
|
||||
enc := json.NewEncoder(w)
|
||||
return enc.Encode(files)
|
||||
}
|
||||
} else if r.Method == "DELETE" {
|
||||
servicename := r.FormValue("service")
|
||||
key := r.FormValue("key")
|
||||
if len(servicename) == 0 || len(key) == 0 {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := caller.mg.mongoClient.Delete(CollectionFile, bson.M{
|
||||
"service": servicename,
|
||||
"key": key,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var seq = uint32(0)
|
||||
|
||||
func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
serviceid := r.FormValue("service")
|
||||
if len(serviceid) == 0 {
|
||||
serviceid = "000000000000"
|
||||
}
|
||||
if r.Method == "PUT" {
|
||||
servicename := r.FormValue("service")
|
||||
hasher := md5.New()
|
||||
hasher.Write([]byte(servicename))
|
||||
subfolder := hex.EncodeToString(hasher.Sum(nil))[:8]
|
||||
|
||||
infile, header, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
@ -216,13 +265,17 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
|
||||
var b [5]byte
|
||||
binary.BigEndian.PutUint32(b[0:4], uint32(time.Now().Unix()))
|
||||
b[4] = byte(atomic.AddUint32(&seq, 1) % 255)
|
||||
rf := hex.EncodeToString(b[:])
|
||||
rf := hex.EncodeToString(b[1:])
|
||||
newidstr := subfolder + rf
|
||||
newidbt, _ := hex.DecodeString(newidstr)
|
||||
newidobj := primitive.NewObjectID()
|
||||
copy(newidobj[:], newidbt[:8])
|
||||
|
||||
var link string
|
||||
if extract {
|
||||
link = path.Join("static", serviceid, rf)
|
||||
link = path.Join("static", subfolder, rf)
|
||||
} else {
|
||||
link = path.Join("static", serviceid, rf, header.Filename)
|
||||
link = path.Join("static", subfolder, rf, header.Filename)
|
||||
}
|
||||
|
||||
newdoc := fileDocumentDesc{
|
||||
@ -233,10 +286,11 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
|
||||
Link: link,
|
||||
Desc: desc,
|
||||
Key: rf,
|
||||
Service: serviceid,
|
||||
Service: servicename,
|
||||
}
|
||||
_, _, err = caller.mg.mongoClient.UpsertOne(CollectionFile, bson.M{
|
||||
"service": serviceid,
|
||||
"_id": newidobj,
|
||||
"service": servicename,
|
||||
"key": rf,
|
||||
}, newdoc)
|
||||
|
||||
@ -247,6 +301,8 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (caller apiCaller) whitelistAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
mg := caller.mg
|
||||
@ -387,6 +443,51 @@ func (caller apiCaller) serviceAPI(w http.ResponseWriter, r *http.Request) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (caller apiCaller) maintenanceAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
mg := caller.mg
|
||||
queryvals := r.URL.Query()
|
||||
if r.Method == "GET" {
|
||||
name := queryvals.Get("name")
|
||||
if len(name) > 0 {
|
||||
if valid, _ := caller.isValidUser(name, "*"); !valid {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return nil
|
||||
}
|
||||
|
||||
if valid, admin := caller.isValidUser(name, "maintenance"); valid || admin {
|
||||
w.Header().Add("MG-X-SERVICE-EDITABLE", name)
|
||||
}
|
||||
|
||||
serptr := atomic.LoadPointer(&mg.services.get(name).divisionsSerialized)
|
||||
w.Write(*(*[]byte)(serptr))
|
||||
} else {
|
||||
caller.writeAccessableServices(w)
|
||||
}
|
||||
} else if r.Method == "POST" {
|
||||
servicename := r.FormValue("name")
|
||||
|
||||
var divs map[string]*division
|
||||
dec := json.NewDecoder(r.Body)
|
||||
if err := dec.Decode(&divs); err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err := mg.mongoClient.Update(CollectionService, bson.M{
|
||||
"service": servicename,
|
||||
}, bson.M{
|
||||
"$set": bson.M{"divisions": divs},
|
||||
}, options.Update().SetUpsert(false))
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (caller apiCaller) accountAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
mg := caller.mg
|
||||
queryvals := r.URL.Query()
|
||||
@ -604,6 +705,8 @@ func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) {
|
||||
err = caller.accountAPI(w, r)
|
||||
} else if strings.HasSuffix(r.URL.Path, "/upload") {
|
||||
err = caller.uploadAPI(w, r)
|
||||
} else if strings.HasSuffix(r.URL.Path, "/maintenance") {
|
||||
err = caller.maintenanceAPI(w, r)
|
||||
} else if strings.HasSuffix(r.URL.Path, "/files") {
|
||||
err = caller.filesAPI(w, r)
|
||||
}
|
||||
|
||||
@ -172,14 +172,17 @@ type division struct {
|
||||
Maintenance *maintenance `bson:",omitempty" json:",omitempty"`
|
||||
}
|
||||
|
||||
type serviceDescription struct {
|
||||
// sync.Mutex
|
||||
type ServiceDescriptionSummary struct {
|
||||
Id primitive.ObjectID `bson:"_id"`
|
||||
ServiceName string `bson:"service"`
|
||||
Divisions map[string]*division `bson:"divisions"`
|
||||
ServiceCode string `bson:"code"`
|
||||
UseWhitelist bool `bson:"use_whitelist"`
|
||||
Closed bool `bson:"closed"`
|
||||
}
|
||||
|
||||
type serviceDescription struct {
|
||||
ServiceDescriptionSummary `bson:",inline"`
|
||||
Divisions map[string]*division `bson:"divisions"`
|
||||
ServerApiTokens []primitive.ObjectID `bson:"api_tokens"`
|
||||
ApiUsers map[string][]string `bson:"api_users"`
|
||||
|
||||
@ -327,7 +330,7 @@ func (sh *serviceDescription) prepare(mg *Maingate) error {
|
||||
mg.apiTokenToService.add(keyid.Hex(), sh.ServiceCode)
|
||||
}
|
||||
|
||||
bt, _ := json.Marshal(sh)
|
||||
bt, _ := json.Marshal(sh.ServiceDescriptionSummary)
|
||||
atomic.StorePointer(&sh.serviceSerialized, unsafe.Pointer(&bt))
|
||||
|
||||
logger.Println("service is ready :", sh.ServiceName, sh.ServiceCode, sh.UseWhitelist, sh.ApiUsers, string(divmarshaled))
|
||||
|
||||
@ -2,7 +2,10 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@ -32,6 +35,10 @@ type servicePipelineDocument struct {
|
||||
}
|
||||
|
||||
type filePipelineDocument struct {
|
||||
OperationType string `bson:"operationType"`
|
||||
DocumentKey struct {
|
||||
Id primitive.ObjectID `bson:"_id"`
|
||||
} `bson:"documentKey"`
|
||||
File *fileDocumentDesc `bson:"fullDocument"`
|
||||
}
|
||||
|
||||
@ -140,6 +147,7 @@ func (mg *Maingate) watchFileCollection(parentctx context.Context, serveMux *htt
|
||||
Key: "$match", Value: bson.D{
|
||||
{Key: "operationType", Value: bson.D{
|
||||
{Key: "$in", Value: bson.A{
|
||||
"delete",
|
||||
"insert",
|
||||
}},
|
||||
}},
|
||||
@ -148,6 +156,8 @@ func (mg *Maingate) watchFileCollection(parentctx context.Context, serveMux *htt
|
||||
projectStage := bson.D{
|
||||
{
|
||||
Key: "$project", Value: bson.D{
|
||||
{Key: "operationType", Value: 1},
|
||||
{Key: "documentKey", Value: 1},
|
||||
{Key: "fullDocument", Value: 1},
|
||||
},
|
||||
},
|
||||
@ -186,7 +196,17 @@ func (mg *Maingate) watchFileCollection(parentctx context.Context, serveMux *htt
|
||||
|
||||
var data filePipelineDocument
|
||||
if err := stream.Decode(&data); err == nil {
|
||||
switch data.OperationType {
|
||||
case "insert":
|
||||
data.File.save()
|
||||
|
||||
case "delete":
|
||||
subfolder := hex.EncodeToString(data.DocumentKey.Id[:4])
|
||||
rf := hex.EncodeToString(data.DocumentKey.Id[4:8])
|
||||
|
||||
subpath := path.Join("static", subfolder, rf)
|
||||
os.RemoveAll(subpath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user