Skip to content

Commit

Permalink
feat: add more manage features
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelncui committed Oct 4, 2023
1 parent c105763 commit 973cbb9
Show file tree
Hide file tree
Showing 20 changed files with 423 additions and 110 deletions.
2 changes: 1 addition & 1 deletion apis/file_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (api *API) FileGet(ctx context.Context, req *entity.FileGetRequest) (*entit
return nil, err
}

children, err := api.lib.List(ctx, req.Id)
children, err := api.lib.ListWithSize(ctx, req.Id)
if err != nil {
return nil, err
}
Expand Down
30 changes: 1 addition & 29 deletions entity/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,14 @@ package entity
import (
"database/sql/driver"
"fmt"
reflect "reflect"
sync "sync"

"github.com/modern-go/reflect2"
"google.golang.org/protobuf/proto"
)

var (
typeMap sync.Map
)

// Scan implement database/sql.Scanner
func Scan(dst proto.Message, src interface{}) error {
cacheKey := reflect2.RTypeOf(dst)
typ, has := loadType(cacheKey)
if !has {
ptrType := reflect.TypeOf(dst)
if ptrType.Kind() != reflect.Ptr {
return fmt.Errorf("scan dst is not an ptr, has= %T", dst)
}

typ = reflect2.Type2(ptrType.Elem())
storeType(cacheKey, typ)
}
typ := reflect2.TypeOf(dst).(reflect2.PtrType).Elem()
typ.Set(dst, typ.New())

var buf []byte
Expand Down Expand Up @@ -59,15 +43,3 @@ func Value(src proto.Message) (driver.Value, error) {
}
return buf, nil
}

func loadType(key uintptr) (reflect2.Type, bool) {
i, has := typeMap.Load(key)
if !has {
return nil, false
}
return i.(reflect2.Type), true
}

func storeType(key uintptr, typ reflect2.Type) {
typeMap.Store(key, typ)
}
4 changes: 2 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"gen-proto": "protoc --ts_out ./src/entity --proto_path ../entity/ `ls ../entity/*.proto` && ./src/entity/gen_index.sh"
},
"dependencies": {
"@aperturerobotics/chonky": "^0.2.6",
"@aperturerobotics/chonky-icon-fontawesome": "^0.2.2",
"@samuelncui/chonky": "^0.2.7",
"@samuelncui/chonky-icon-fontawesome": "^0.2.7",
"@emotion/react": "^11.10.4",
"@emotion/styled": "^11.10.4",
"@fortawesome/fontawesome-svg-core": "^1.2.32",
Expand Down
14 changes: 11 additions & 3 deletions frontend/src/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FileData, FileArray, FileAction } from "@aperturerobotics/chonky";
import { defineFileAction } from "@aperturerobotics/chonky";
import { ChonkyActions } from "@aperturerobotics/chonky";
import { FileData, FileArray, FileAction } from "@samuelncui/chonky";
import { defineFileAction } from "@samuelncui/chonky";
import { ChonkyActions } from "@samuelncui/chonky";

type RenameFileState = {
contextMenuTriggerFile: FileData;
Expand Down Expand Up @@ -46,3 +46,11 @@ export const CreateRestoreJobAction = defineFileAction({
toolbar: true,
},
} as FileAction);

export const TrimLibraryAction = defineFileAction({
id: "trim_library",
button: {
name: "Trim Library",
toolbar: true,
},
} as FileAction);
4 changes: 2 additions & 2 deletions frontend/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FileData } from "@aperturerobotics/chonky";
import { FileData } from "@samuelncui/chonky";
import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
import { ServiceClient, File, SourceFile, Tape, Position } from "./entity";

Expand Down Expand Up @@ -90,7 +90,7 @@ export function convertTapes(tapes: Array<Tape>): FileData[] {
selectable: true,
draggable: false,
droppable: false,
size: 0,
size: Number(tape.writenBytes),
modDate: moment.unix(Number(tape.createTime)).toDate(),
isTape: true,
};
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/components/toolbarInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { memo } from "react";
import Typography from "@mui/material/Typography";
import { FileArray } from "@samuelncui/chonky";

import { formatFilesize } from "../tools";

export interface ToobarInfoProps {
files?: FileArray;
}

export const ToobarInfo: React.FC<ToobarInfoProps> = memo((props) => {
return (
<div className="chonky-infoContainer">
<Typography variant="body1" className="chonky-infoText">
{formatFilesize((props.files || []).reduce((total, file) => total + (file?.size ? file.size : 0), 0))}
</Typography>
</div>
);
});
4 changes: 2 additions & 2 deletions frontend/src/init.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { setChonkyDefaults } from "@aperturerobotics/chonky";
import { ChonkyIconFA } from "@aperturerobotics/chonky-icon-fontawesome";
import { setChonkyDefaults } from "@samuelncui/chonky";
import { ChonkyIconFA } from "@samuelncui/chonky-icon-fontawesome";

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPencilAlt } from "@fortawesome/free-solid-svg-icons/faPencilAlt";
Expand Down
8 changes: 3 additions & 5 deletions frontend/src/pages/backup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { useState, useEffect, useMemo, useCallback, FC, useRef, RefObject } from

import Grid from "@mui/material/Grid";
import Box from "@mui/material/Box";
import { FileBrowser, FileNavbar, FileToolbar, FileList, FileContextMenu, FileArray, FileBrowserHandle } from "@aperturerobotics/chonky";
import { ChonkyActions, ChonkyFileActionData, FileData } from "@aperturerobotics/chonky";
import { FileBrowser, FileNavbar, FileToolbar, FileList, FileContextMenu, FileArray, FileBrowserHandle } from "@samuelncui/chonky";
import { ChonkyActions, ChonkyFileActionData, FileData } from "@samuelncui/chonky";

import { cli, convertSourceFiles } from "../api";
import { Root } from "../api";
Expand All @@ -26,7 +26,6 @@ const useBackupSourceBrowser = (source: RefObject<FileBrowserHandle>) => {

const onFileAction = useCallback(
(data: ChonkyFileActionData) => {
console.log("source", data);
switch (data.id) {
case ChonkyActions.OpenFiles.id:
(async () => {
Expand Down Expand Up @@ -72,7 +71,7 @@ const useBackupTargetBrowser = () => {
const [files, setFiles] = useState<FileArray>(Array(0));
const [folderChain, setFolderChan] = useState<FileArray>([
{
id: "0",
id: "backup_waitlist",
name: "Backup Waitlist",
isDir: true,
openable: true,
Expand All @@ -84,7 +83,6 @@ const useBackupTargetBrowser = () => {

const onFileAction = useCallback(
(data: ChonkyFileActionData) => {
console.log("target", data);
switch (data.id) {
case ChonkyActions.DeleteFiles.id:
(() => {
Expand Down
27 changes: 23 additions & 4 deletions frontend/src/pages/file.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { useState, useRef, useEffect, useMemo, useCallback } from "react";

import Grid from "@mui/material/Grid";
import Box from "@mui/material/Box";
import { FullFileBrowser, FileBrowserProps, FileBrowserHandle, FileArray } from "@aperturerobotics/chonky";
import { ChonkyActions, ChonkyFileActionData } from "@aperturerobotics/chonky";
import { FileBrowser as ChonckFileBrowser, FileNavbar, FileToolbar, FileList, FileContextMenu, FileArray, FileBrowserHandle } from "@samuelncui/chonky";
import { ChonkyActions, ChonkyFileActionData } from "@samuelncui/chonky";

import { cli, convertFiles } from "../api";
import { Root } from "../api";
import { RenameFileAction, RefreshListAction } from "../actions";
import { ToobarInfo } from "../components/toolbarInfo";

import { useDetailModal, DetailModal } from "./file-detail";
import { FileGetReply } from "../entity";
Expand Down Expand Up @@ -152,6 +153,9 @@ const useFileBrowser = (storageKey: string, refreshAll: () => Promise<void>, ope
);

const fileActions = useMemo(() => [ChonkyActions.CreateFolder, ChonkyActions.DeleteFiles, ChonkyActions.MoveFiles, RenameFileAction, RefreshListAction], []);
const totalSize = useMemo(() => {
return files.reduce((total, file) => total + (file?.size ? file.size : 0), 0);
}, [files]);

return {
files,
Expand All @@ -160,6 +164,7 @@ const useFileBrowser = (storageKey: string, refreshAll: () => Promise<void>, ope
fileActions,
defaultFileViewActionId: ChonkyActions.EnableListView.id,
doubleClickDelay: 300,
totalSize,
};
};

Expand All @@ -185,10 +190,24 @@ export const FileBrowser = () => {
<Box className="browser-box">
<Grid className="browser-container" container>
<Grid className="browser" item xs={6}>
<FullFileBrowser instanceId="left" ref={instances.left} {...leftProps} />
<ChonckFileBrowser instanceId="left" ref={instances.left} {...leftProps}>
<FileNavbar />
<FileToolbar>
<ToobarInfo {...leftProps} />
</FileToolbar>
<FileList />
<FileContextMenu />
</ChonckFileBrowser>
</Grid>
<Grid className="browser" item xs={6}>
<FullFileBrowser instanceId="right" ref={instances.right} {...rightProps} />
<ChonckFileBrowser instanceId="right" ref={instances.right} {...rightProps}>
<FileNavbar />
<FileToolbar>
<ToobarInfo {...rightProps} />
</FileToolbar>
<FileList />
<FileContextMenu />
</ChonckFileBrowser>
</Grid>
</Grid>
<DetailModal detail={detail} onClose={closeDetailModel} />
Expand Down
43 changes: 27 additions & 16 deletions frontend/src/pages/restore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@ import { useState, useEffect, useMemo, useCallback, FC, useRef, RefObject } from

import Grid from "@mui/material/Grid";
import Box from "@mui/material/Box";
import { FileBrowser, FileNavbar, FileToolbar, FileList, FileContextMenu, FileArray, FileBrowserHandle } from "@aperturerobotics/chonky";
import { ChonkyActions, ChonkyFileActionData, FileData } from "@aperturerobotics/chonky";
import { FileBrowser, FileNavbar, FileToolbar, FileList, FileContextMenu, FileArray, FileBrowserHandle } from "@samuelncui/chonky";
import { ChonkyActions, ChonkyFileActionData, FileData } from "@samuelncui/chonky";

import { ToobarInfo } from "../components/toolbarInfo";

import { cli, convertFiles } from "../api";
import { Root } from "../api";
import { AddFileAction, RefreshListAction, CreateRestoreJobAction } from "../actions";
import { JobCreateRequest, JobRestoreParam, Source } from "../entity";

const useRestoreSourceBrowser = (source: RefObject<FileBrowserHandle>) => {
const useRestoreSourceBrowser = (target: RefObject<FileBrowserHandle>) => {
const [files, setFiles] = useState<FileArray>(Array(1).fill(null));
const [folderChain, setFolderChan] = useState<FileArray>([Root]);

const openFolder = useCallback(async (id: string) => {
const [file, folderChain] = await Promise.all([cli.fileGet({ id: BigInt(id) }).response, cli.fileListParents({ id: BigInt(id) }).response]);

setFiles(convertFiles(file.children));
setFiles(convertFiles(file.children).map((file) => ({ ...file, droppable: false })));
setFolderChan([Root, ...convertFiles(folderChain.parents)]);
}, []);
useEffect(() => {
Expand All @@ -26,7 +28,6 @@ const useRestoreSourceBrowser = (source: RefObject<FileBrowserHandle>) => {

const onFileAction = useCallback(
(data: ChonkyFileActionData) => {
console.log("source", data);
switch (data.id) {
case ChonkyActions.OpenFiles.id:
(async () => {
Expand All @@ -45,24 +46,31 @@ const useRestoreSourceBrowser = (source: RefObject<FileBrowserHandle>) => {

return;
case ChonkyActions.EndDragNDrop.id:
(() => {
if (!source.current) {
(async () => {
if (!target.current) {
return;
}

const base = folderChain
.filter((file): file is FileData => !!file && file.id !== "0")
.map((file) => file.name)
.join("/");
source.current.requestFileAction(AddFileAction, {
...data.payload,
selectedFiles: data.payload.selectedFiles.map((file) => ({ ...file, name: base + "/" + file.name })),
});

const selectedFiles = data.payload.selectedFiles.map((file) => ({
...file,
name: base ? base + "/" + file.name : file.name,
openable: false,
draggable: false,
}));
await target.current.requestFileAction(AddFileAction, { ...data.payload, selectedFiles });
})();

return;
}

console.log("source done", data);
},
[openFolder, source, folderChain],
[openFolder, target, folderChain],
);

const fileActions = useMemo(() => [ChonkyActions.StartDragNDrop, RefreshListAction], []);
Expand All @@ -81,7 +89,7 @@ const useRestoreTargetBrowser = () => {
const [files, setFiles] = useState<FileArray>(Array(0));
const [folderChain, setFolderChan] = useState<FileArray>([
{
id: "0",
id: "restore_waitlist",
name: "Restore Waitlist",
isDir: true,
openable: true,
Expand All @@ -93,7 +101,6 @@ const useRestoreTargetBrowser = () => {

const onFileAction = useCallback(
(data: ChonkyFileActionData) => {
console.log("target", data);
switch (data.id) {
case ChonkyActions.DeleteFiles.id:
(() => {
Expand Down Expand Up @@ -141,15 +148,19 @@ export const RestoreBrowser = () => {
<Grid className="browser" item xs={6}>
<FileBrowser {...sourceProps}>
<FileNavbar />
<FileToolbar />
<FileToolbar>
<ToobarInfo {...sourceProps} />
</FileToolbar>
<FileList />
<FileContextMenu />
</FileBrowser>
</Grid>
<Grid className="browser" item xs={6}>
<FileBrowser {...targetProps} ref={target}>
<FileNavbar />
<FileToolbar />
<FileToolbar>
<ToobarInfo {...targetProps} />
</FileToolbar>
<FileList />
<FileContextMenu />
</FileBrowser>
Expand Down
20 changes: 14 additions & 6 deletions frontend/src/pages/tapes.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useState, useEffect, useMemo, useCallback, FC, useRef, RefObject } from "react";
import moment from "moment";

import Grid from "@mui/material/Grid";
import Box from "@mui/material/Box";
import { FileBrowser, FileNavbar, FileToolbar, FileList, FileContextMenu, FileArray, FileBrowserHandle } from "@aperturerobotics/chonky";
import { ChonkyActions, ChonkyFileActionData, FileData } from "@aperturerobotics/chonky";
import { FileBrowser, FileNavbar, FileToolbar, FileList, FileContextMenu, FileArray, FileBrowserHandle } from "@samuelncui/chonky";
import { ChonkyActions, ChonkyFileActionData, FileData } from "@samuelncui/chonky";

import { cli, Root, convertTapes, convertPositions } from "../api";
import { TapeListRequest, Source, Tape, Position } from "../entity";
import { TrimLibraryAction } from "../actions";

export const TapesType = "tapes";

Expand Down Expand Up @@ -49,7 +48,6 @@ const useTapesSourceBrowser = (source: RefObject<FileBrowserHandle>) => {

const reply = await cli.tapeGetPositions({ id: BigInt(tapeIDStr), directory: dir }).response;
const files = convertPositions(reply.positions);
console.log("refresh jobs list, target= ", target, "tape_id= ", tapeIDStr, "dir= ", dir, "reply= ", reply, "files= ", files);
setFiles(files);

const targetFolderChain = [];
Expand Down Expand Up @@ -105,12 +103,22 @@ const useTapesSourceBrowser = (source: RefObject<FileBrowserHandle>) => {
await openFolder(current);
})();
return;
case TrimLibraryAction.id:
(async () => {
if (!confirm(`Empty pointer in library will be trimed, may cause data loss. Are you sure?`)) {
return;
}

console.log(await cli.libraryTrim({ trimFile: true, trimPosition: true }).response);
alert("Trim Library Success!");
})();
return;
}
},
[openFolder, source, folderChain],
);

const fileActions = useMemo(() => [ChonkyActions.DeleteFiles], []);
const fileActions = useMemo(() => [ChonkyActions.DeleteFiles, TrimLibraryAction], []);

return {
files,
Expand Down
Loading

0 comments on commit 973cbb9

Please sign in to comment.