feat(multi-home): enable separated home dir for each user (#64)

* feat(files): make files service supporting home dir

* fix(files): add path access control and avoid redirecting path in the backend

* feat(files): add ListHome API

* fix(server): fix access control issues

* feat(client/web): support multi-home

* feat(server): cleanup

* fix(server): failed to init admin folder
This commit is contained in:
Hexxa 2021-07-24 21:05:36 -05:00 committed by GitHub
parent 9748d0cab4
commit 81da97650b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 527 additions and 212 deletions

View file

@ -10,15 +10,17 @@ import (
)
type FilesClient struct {
addr string
r *gorequest.SuperAgent
addr string
r *gorequest.SuperAgent
token *http.Cookie
}
func NewFilesClient(addr string) *FilesClient {
func NewFilesClient(addr string, token *http.Cookie) *FilesClient {
gr := gorequest.New()
return &FilesClient{
addr: addr,
r: gr,
addr: addr,
r: gr,
token: token,
}
}
@ -28,6 +30,7 @@ func (cl *FilesClient) url(urlpath string) string {
func (cl *FilesClient) Create(filepath string, size int64) (*http.Response, string, []error) {
return cl.r.Post(cl.url("/v1/fs/files")).
AddCookie(cl.token).
Send(fileshdr.CreateReq{
Path: filepath,
FileSize: size,
@ -37,12 +40,14 @@ func (cl *FilesClient) Create(filepath string, size int64) (*http.Response, stri
func (cl *FilesClient) Delete(filepath string) (*http.Response, string, []error) {
return cl.r.Delete(cl.url("/v1/fs/files")).
AddCookie(cl.token).
Param(fileshdr.FilePathQuery, filepath).
End()
}
func (cl *FilesClient) Metadata(filepath string) (*http.Response, *fileshdr.MetadataResp, []error) {
resp, body, errs := cl.r.Get(cl.url("/v1/fs/metadata")).
AddCookie(cl.token).
Param(fileshdr.FilePathQuery, filepath).
End()
@ -57,12 +62,14 @@ func (cl *FilesClient) Metadata(filepath string) (*http.Response, *fileshdr.Meta
func (cl *FilesClient) Mkdir(dirpath string) (*http.Response, string, []error) {
return cl.r.Post(cl.url("/v1/fs/dirs")).
AddCookie(cl.token).
Send(fileshdr.MkdirReq{Path: dirpath}).
End()
}
func (cl *FilesClient) Move(oldpath, newpath string) (*http.Response, string, []error) {
return cl.r.Patch(cl.url("/v1/fs/files/move")).
AddCookie(cl.token).
Send(fileshdr.MoveReq{
OldPath: oldpath,
NewPath: newpath,
@ -72,6 +79,7 @@ func (cl *FilesClient) Move(oldpath, newpath string) (*http.Response, string, []
func (cl *FilesClient) UploadChunk(filepath string, content string, offset int64) (*http.Response, string, []error) {
return cl.r.Patch(cl.url("/v1/fs/files/chunks")).
AddCookie(cl.token).
Send(fileshdr.UploadChunkReq{
Path: filepath,
Content: content,
@ -82,6 +90,7 @@ func (cl *FilesClient) UploadChunk(filepath string, content string, offset int64
func (cl *FilesClient) UploadStatus(filepath string) (*http.Response, *fileshdr.UploadStatusResp, []error) {
resp, body, errs := cl.r.Get(cl.url("/v1/fs/files/chunks")).
AddCookie(cl.token).
Param(fileshdr.FilePathQuery, filepath).
End()
@ -96,6 +105,7 @@ func (cl *FilesClient) UploadStatus(filepath string) (*http.Response, *fileshdr.
func (cl *FilesClient) Download(filepath string, headers map[string]string) (*http.Response, string, []error) {
r := cl.r.Get(cl.url("/v1/fs/files")).
AddCookie(cl.token).
Param(fileshdr.FilePathQuery, filepath)
for key, val := range headers {
r = r.Set(key, val)
@ -105,6 +115,7 @@ func (cl *FilesClient) Download(filepath string, headers map[string]string) (*ht
func (cl *FilesClient) List(dirPath string) (*http.Response, *fileshdr.ListResp, []error) {
resp, body, errs := cl.r.Get(cl.url("/v1/fs/dirs")).
AddCookie(cl.token).
Param(fileshdr.ListDirQuery, dirPath).
End()
if len(errs) > 0 {
@ -121,6 +132,7 @@ func (cl *FilesClient) List(dirPath string) (*http.Response, *fileshdr.ListResp,
func (cl *FilesClient) ListUploadings() (*http.Response, *fileshdr.ListUploadingsResp, []error) {
resp, body, errs := cl.r.Get(cl.url("/v1/fs/uploadings")).
AddCookie(cl.token).
End()
if len(errs) > 0 {
return nil, nil, errs
@ -136,6 +148,7 @@ func (cl *FilesClient) ListUploadings() (*http.Response, *fileshdr.ListUploading
func (cl *FilesClient) DelUploading(filepath string) (*http.Response, string, []error) {
return cl.r.Delete(cl.url("/v1/fs/uploadings")).
AddCookie(cl.token).
Param(fileshdr.FilePathQuery, filepath).
End()
}

View file

@ -136,6 +136,14 @@ export class FilesClient extends BaseClient {
});
};
listHome = (): Promise<Response<ListResp>> => {
return this.do({
method: "get",
url: `${this.url}/v1/fs/dirs/home`,
params: {},
});
};
listUploadings = (): Promise<Response<ListUploadingsResp>> => {
return this.do({
method: "get",

View file

@ -19,6 +19,7 @@ export class FilesClient {
private uploadStatusMockResps: Array<Promise<Response<UploadStatusResp>>>;
private uploadStatusMockRespID: number = 0;
private listMockResp: Promise<Response<ListResp>>;
private listHomeMockResp: Promise<Response<ListResp>>;
private listUploadingsMockResp: Promise<Response<ListUploadingsResp>>;
private deleteUploadingMockResp: Promise<Response>;
@ -58,6 +59,10 @@ export class FilesClient {
this.listMockResp = resp;
};
listHomeMock = (resp: Promise<Response<ListResp>>) => {
this.listMockResp = resp;
};
listUploadingsMock = (resp: Promise<Response<ListUploadingsResp>>) => {
this.listUploadingsMockResp = resp;
}
@ -111,6 +116,10 @@ export class FilesClient {
return this.listMockResp;
};
listHome = (): Promise<Response<ListResp>> => {
return this.listHomeMockResp;
};
listUploadings = (): Promise<Response<ListUploadingsResp>> => {
return this.listUploadingsMockResp;
};

View file

@ -17,6 +17,7 @@ export interface UploadStatusResp {
}
export interface ListResp {
cwd: string;
metadatas: MetadataResp[];
}
@ -50,6 +51,7 @@ export interface IFilesClient {
) => Promise<Response<UploadStatusResp>>;
uploadStatus: (filePath: string) => Promise<Response<UploadStatusResp>>;
list: (dirPath: string) => Promise<Response<ListResp>>;
listHome: () => Promise<Response<ListResp>>;
listUploadings: () => Promise<Response<ListUploadingsResp>>;
deleteUploading: (filePath: string) => Promise<Response>;
}

View file

@ -114,6 +114,27 @@ export class Updater {
: this.props.items;
};
setHomeItems = async (): Promise<void> => {
const listResp = await this.filesClient.listHome();
this.props.dirPath = List<string>(listResp.data.cwd.split("/"));
this.props.items =
listResp.status === 200
? List<MetadataResp>(listResp.data.metadatas)
: this.props.items;
};
goHome = async (): Promise<void> => {
const listResp = await this.filesClient.listHome();
// how to get current dir? to dirPath?
// this.props.dirPath = dirParts;
this.props.items =
listResp.status === 200
? List<MetadataResp>(listResp.data.metadatas)
: this.props.items;
};
moveHere = async (
srcDir: string,
dstDir: string,

View file

@ -104,9 +104,7 @@ export class AuthPane extends React.Component<Props, State, {}> {
this.update(PanesUpdater.updateState);
// refresh
return BrowserUpdater().setItems(
List<string>(["."])
);
return BrowserUpdater().setHomeItems();
} else {
this.setState({ user: "", pwd: "" });
alert("Failed to login.");

View file

@ -20,7 +20,7 @@ export class StateMgr extends React.Component<Props, State, {}> {
BrowserUpdater().init(state.panel.browser);
BrowserUpdater().setClients(new UsersClient(""), new FilesClient(""));
BrowserUpdater()
.setItems(state.panel.browser.dirPath)
.setHomeItems()
.then(() => {
return BrowserUpdater().refreshUploadings();
})