Skip to content

Commit

Permalink
Go add upload link API
Browse files Browse the repository at this point in the history
  • Loading branch information
杨赫然 committed Aug 20, 2024
1 parent 59e2ee5 commit d33cf74
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 9 deletions.
2 changes: 2 additions & 0 deletions common/seaf-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ load_seahub_private_key (SeafileSession *session, const char *conf_dir)
session->seahub_url = g_strdup("http://127.0.0.1:8000/api/v2.1/internal");
}
session->seahub_conn_pool = connection_pool_new ();
} else {
seaf_warning ("No seahub private key is configured.\n");
}
g_free (site_root);

Expand Down
256 changes: 254 additions & 2 deletions fileserver/fileop.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,10 @@ func doBlock(rsp http.ResponseWriter, r *http.Request, repo *repomgr.Repo, fileI
}

func accessZipCB(rsp http.ResponseWriter, r *http.Request) *appError {
if seahubPK == "" {
err := fmt.Errorf("no seahub private key is configured")
return &appError{err, "", http.StatusNotFound}
}
parts := strings.Split(r.URL.Path[1:], "/")
if len(parts) != 4 {
msg := "Invalid URL"
Expand Down Expand Up @@ -681,6 +685,7 @@ func accessZipCB(rsp http.ResponseWriter, r *http.Request) *appError {
return nil
}

// Go don't need the zip API.
func accessZipLinkCB(rsp http.ResponseWriter, r *http.Request) *appError {
return &appError{nil, "", http.StatusNotFound}
}
Expand Down Expand Up @@ -3418,9 +3423,10 @@ func indexRawBlocks(repoID string, blockIDs []string, fileHeaders []*multipart.F
return nil
}

func uploadLinksCB(rsp http.ResponseWriter, r *http.Request) *appError {
func uploadLinkCB(rsp http.ResponseWriter, r *http.Request) *appError {
if seahubPK == "" {
return &appError{nil, "", http.StatusNotFound}
err := fmt.Errorf("no seahub private key is configured")
return &appError{err, "", http.StatusNotFound}
}
if r.Method == "OPTIONS" {
setAccessControl(rsp)
Expand Down Expand Up @@ -3534,6 +3540,252 @@ func queryAccessToken(token, opType string) (*ShareLinkInfo, *appError) {
return info, nil
}

func accessLinkCB(rsp http.ResponseWriter, r *http.Request) *appError {
if seahubPK == "" {
err := fmt.Errorf("no seahub private key is configured")
return &appError{err, "", http.StatusNotFound}
}

parts := strings.Split(r.URL.Path[1:], "/")
if len(parts) < 2 {
msg := "Invalid URL"
return &appError{nil, msg, http.StatusBadRequest}
}
token := parts[1]
info, appErr := queryAccessToken(token, "file")
if appErr != nil {
return appErr
}

if info.FilePath == "" {
msg := "Invalid file_path\n"
return &appError{nil, msg, http.StatusBadRequest}
}

repoID := info.RepoID
filePath := normalizeUTF8Path(info.FilePath)
fileName := filepath.Base(filePath)
op := "download-link"

if _, ok := r.Header["If-Modified-Since"]; ok {
return &appError{nil, "", http.StatusNotModified}
}

now := time.Now()
rsp.Header().Set("Last-Modified", now.Format("Mon, 2 Jan 2006 15:04:05 GMT"))
rsp.Header().Set("Cache-Control", "max-age=3600")

ranges := r.Header["Range"]
byteRanges := strings.Join(ranges, "")

repo := repomgr.Get(repoID)
if repo == nil {
msg := "Bad repo id\n"
return &appError{nil, msg, http.StatusBadRequest}
}

user, _ := repomgr.GetRepoOwner(repoID)

fileID, _, err := fsmgr.GetObjIDByPath(repo.StoreID, repo.RootID, filePath)
if err != nil {
msg := "Invalid file_path\n"
return &appError{nil, msg, http.StatusBadRequest}
}
rsp.Header().Set("ETag", fileID)

var cryptKey *seafileCrypt
if repo.IsEncrypted {
key, err := parseCryptKey(rsp, repoID, user, repo.EncVersion)
if err != nil {
return err
}
cryptKey = key
}

exists, _ := fsmgr.Exists(repo.StoreID, fileID)
if !exists {
msg := "Invalid file id"
return &appError{nil, msg, http.StatusBadRequest}
}

if !repo.IsEncrypted && len(byteRanges) != 0 {
if err := doFileRange(rsp, r, repo, fileID, fileName, op, byteRanges, user); err != nil {
return err
}
} else if err := doFile(rsp, r, repo, fileID, fileName, op, cryptKey, user); err != nil {
return err
}

return nil
}

func accessDirLinkCB(rsp http.ResponseWriter, r *http.Request) *appError {
if seahubPK == "" {
err := fmt.Errorf("no seahub private key is configured")
return &appError{err, "", http.StatusNotFound}
}

parts := strings.Split(r.URL.Path[1:], "/")
if len(parts) < 2 {
msg := "Invalid URL"
return &appError{nil, msg, http.StatusBadRequest}
}
token := parts[1]
info, appErr := queryAccessToken(token, "dir")
if appErr != nil {
return appErr
}

repoID := info.RepoID
parentDir := normalizeUTF8Path(info.ParentDir)
op := "download-link"

repo := repomgr.Get(repoID)
if repo == nil {
msg := "Bad repo id\n"
return &appError{nil, msg, http.StatusBadRequest}
}
user, _ := repomgr.GetRepoOwner(repoID)

filePath := r.URL.Query().Get("p")
if filePath == "" {
err := r.ParseForm()
if err != nil {
msg := "Invalid form\n"
return &appError{nil, msg, http.StatusBadRequest}
}
parentDir := r.FormValue("parent_dir")
if parentDir == "" {
msg := "Invalid parent_dir\n"
return &appError{nil, msg, http.StatusBadRequest}
}
parentDir = normalizeUTF8Path(parentDir)
parentDir = getCanonPath(parentDir)
dirents := r.FormValue("dirents")
if dirents == "" {
msg := "Invalid dirents\n"
return &appError{nil, msg, http.StatusBadRequest}
}
// opStr:=r.FormVale("op")
list, err := jsonToDirentList(repo, parentDir, dirents)
if err != nil {
log.Warnf("failed to parse dirent list: %v", err)
msg := "Invalid dirents\n"
return &appError{nil, msg, http.StatusBadRequest}
}
if len(list) == 0 {
msg := "Invalid dirents\n"
return &appError{nil, msg, http.StatusBadRequest}
}

obj := make(map[string]interface{})
if len(list) == 1 {
dent := list[0]
op = "download-dir-link"
obj["dir_name"] = dent.Name
obj["obj_id"] = dent.ID
} else {
op = "download-multi-link"
obj["parent_dir"] = parentDir
var fileList []string
for _, dent := range list {
fileList = append(fileList, dent.Name)
}
obj["file_list"] = fileList
}
data, err := json.Marshal(obj)
if err != nil {
err := fmt.Errorf("failed to encode zip obj: %v", err)
return &appError{err, "", http.StatusInternalServerError}
}
if err := downloadZipFile(rsp, r, string(data), repoID, user, op); err != nil {
return err
}
return nil
}

// file path is not empty string
if _, ok := r.Header["If-Modified-Since"]; ok {
return &appError{nil, "", http.StatusNotModified}
}

filePath = normalizeUTF8Path(filePath)
fullPath := filepath.Join(parentDir, filePath)
fileName := filepath.Base(filePath)

fileID, _, err := fsmgr.GetObjIDByPath(repo.StoreID, repo.RootID, fullPath)
if err != nil {
msg := "Invalid file_path\n"
return &appError{nil, msg, http.StatusBadRequest}
}
rsp.Header().Set("ETag", fileID)

now := time.Now()
rsp.Header().Set("Last-Modified", now.Format("Mon, 2 Jan 2006 15:04:05 GMT"))
rsp.Header().Set("Cache-Control", "max-age=3600")

ranges := r.Header["Range"]
byteRanges := strings.Join(ranges, "")

var cryptKey *seafileCrypt
if repo.IsEncrypted {
key, err := parseCryptKey(rsp, repoID, user, repo.EncVersion)
if err != nil {
return err
}
cryptKey = key
}

exists, _ := fsmgr.Exists(repo.StoreID, fileID)
if !exists {
msg := "Invalid file id"
return &appError{nil, msg, http.StatusBadRequest}
}

if !repo.IsEncrypted && len(byteRanges) != 0 {
if err := doFileRange(rsp, r, repo, fileID, fileName, op, byteRanges, user); err != nil {
return err
}
} else if err := doFile(rsp, r, repo, fileID, fileName, op, cryptKey, user); err != nil {
return err
}

return nil
}

func jsonToDirentList(repo *repomgr.Repo, parentDir, dirents string) ([]*fsmgr.SeafDirent, error) {
var list []string
err := json.Unmarshal([]byte(dirents), &list)
if err != nil {
return nil, err
}

dir, err := fsmgr.GetSeafdirByPath(repo.StoreID, repo.RootID, parentDir)
if err != nil {
return nil, err
}

direntHash := make(map[string]*fsmgr.SeafDirent)
for _, dent := range dir.Entries {
direntHash[dent.Name] = dent
}

var direntList []*fsmgr.SeafDirent
for _, path := range list {
normPath := normalizeUTF8Path(path)
if normPath == "" || normPath == "/" {
return nil, fmt.Errorf("Invalid download file name: %s\n", normPath)
}
dent, ok := direntHash[normPath]
if !ok {
return nil, fmt.Errorf("failed to get dient for %s in dir %s in repo %s", normPath, parentDir, repo.StoreID)
}
direntList = append(direntList, dent)
}

return direntList, nil
}

func removeFileopExpireCache() {
deleteBlockMaps := func(key interface{}, value interface{}) bool {
if blkMap, ok := value.(*blockMap); ok {
Expand Down
19 changes: 13 additions & 6 deletions fileserver/fileserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,14 @@ func loadSeahubPK() {

scanner := bufio.NewScanner(file)

pkRe, err := regexp.Compile(`SECRET_KEY\\s*=\\s*'([^']*)'`)
pkExp := "SECRET_KEY\\s*=\\s*'([^']*)'"
pkRe, err := regexp.Compile(pkExp)
if err != nil {
log.Warnf("Failed to compile regex: %v", err)
return
}
siteRootRe, err := regexp.Compile(`SITE_ROOT\\s*=\\s*'([^']*)'`)
siteRootExpr := "SITE_ROOT\\s*=\\s*'([^']*)'"
siteRootRe, err := regexp.Compile(siteRootExpr)
if err != nil {
log.Warnf("Failed to compile regex: %v", err)
return
Expand All @@ -305,9 +307,12 @@ func loadSeahubPK() {
}
}
if siteRoot != "" {
seahubURL = fmt.Sprintf("http://127.0.0.1:8000%sapi/v2.1/internal/user-list/", siteRoot)
seahubURL = fmt.Sprintf("http://127.0.0.1:8000%sapi/v2.1/internal", siteRoot)
} else {
seahubURL = ("http://127.0.0.1:8000/api/v2.1/internal/user-list/")
seahubURL = ("http://127.0.0.1:8000/api/v2.1/internal")
}
if seahubPK == "" {
log.Warnf("No seahub private key is configured")
}
}

Expand Down Expand Up @@ -504,7 +509,6 @@ func newHTTPRouter() *mux.Router {
r.Handle("/files/{.*}/{.*}", appHandler(accessCB))
r.Handle("/blks/{.*}/{.*}", appHandler(accessBlksCB))
r.Handle("/zip/{.*}", appHandler(accessZipLinkCB))
r.Handle("/repos/{repoid:[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}}/zip/{.*}", appHandler(accessZipCB))
r.Handle("/upload-api/{.*}", appHandler(uploadAPICB))
r.Handle("/upload-aj/{.*}", appHandler(uploadAjaxCB))
r.Handle("/update-api/{.*}", appHandler(updateAPICB))
Expand All @@ -513,7 +517,10 @@ func newHTTPRouter() *mux.Router {
r.Handle("/upload-raw-blks-api/{.*}", appHandler(uploadRawBlksAPICB))

// links api
r.Handle("/u/{.*}", appHandler(uploadLinksCB))
r.Handle("/u/{.*}", appHandler(uploadLinkCB))
r.Handle("/f/{.*}", appHandler(accessLinkCB))
r.Handle("/d/{.*}", appHandler(accessDirLinkCB))
r.Handle("/repos/{repoid:[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}}/zip/{.*}", appHandler(accessZipCB))

// file syncing api
r.Handle("/repo/{repoid:[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}}/permission-check{slash:\\/?}",
Expand Down
3 changes: 2 additions & 1 deletion fileserver/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,8 @@ func postGetNickName(modifier string) string {
return ""
}

status, body, err := utils.HttpCommon("POST", seahubURL, header, bytes.NewReader(data))
url := seahubURL + "/user-list/"
status, body, err := utils.HttpCommon("POST", url, header, bytes.NewReader(data))
if err != nil {
return ""
}
Expand Down
4 changes: 4 additions & 0 deletions server/access-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -2014,6 +2014,10 @@ access_dir_link_cb(evhtp_request_t *req, void *arg)
goto success;
}

if (can_use_cached_content (req)) {
goto success;
}

parent_dir = seafile_share_link_info_get_parent_dir (info);
norm_parent_dir = normalize_utf8_path (parent_dir);
norm_path = normalize_utf8_path (path);
Expand Down

0 comments on commit d33cf74

Please sign in to comment.