Compare commits
1 Commits
6f15f82122
...
0b58c3347b
Author | SHA1 | Date | |
---|---|---|---|
|
0b58c3347b |
@ -4,7 +4,7 @@ import {VirtualizerScrollView} from "@fluentui/react-components/unstable";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {useStoreBucket} from "../../store/bucket";
|
import {useStoreBucket} from "../../store/bucket";
|
||||||
import {Bucket} from "../../interfaces/connection";
|
import {Bucket} from "../../interfaces/connection";
|
||||||
import {useStoreFile, useStoreFileFilter} from "../../store/file";
|
import {useStoreFile} from "../../store/file";
|
||||||
import {useStoreConnection} from "../../store/connection";
|
import {useStoreConnection} from "../../store/connection";
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
@ -35,13 +35,13 @@ const useStyles = makeStyles({
|
|||||||
export function ListBucketComponent() {
|
export function ListBucketComponent() {
|
||||||
|
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const {conn_active} = useStoreConnection()
|
||||||
const {bucket_set, bucket_list} = useStoreBucket()
|
const {bucket_set, bucket_list} = useStoreBucket()
|
||||||
const {filter_set, prefix_set} = useStoreFileFilter()
|
const {files_get} = useStoreFile()
|
||||||
|
|
||||||
async function handleClick(item: Bucket) {
|
async function handleClick(item: Bucket) {
|
||||||
await bucket_set(item)
|
bucket_set(item)
|
||||||
await filter_set('')
|
files_get(conn_active!, item, "")
|
||||||
await prefix_set('')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRightClick(e: React.MouseEvent<HTMLDivElement>, item: Bucket) {
|
function handleRightClick(e: React.MouseEvent<HTMLDivElement>, item: Bucket) {
|
||||||
@ -50,7 +50,7 @@ export function ListBucketComponent() {
|
|||||||
|
|
||||||
return <MenuList className={styles.container}>
|
return <MenuList className={styles.container}>
|
||||||
<VirtualizerScrollView
|
<VirtualizerScrollView
|
||||||
numItems={bucket_list ? bucket_list.length : 0}
|
numItems={bucket_list?bucket_list.length:0}
|
||||||
itemSize={32}
|
itemSize={32}
|
||||||
container={{role: 'list', style: {maxHeight: 'calc(100vh - 9rem)'}}}
|
container={{role: 'list', style: {maxHeight: 'calc(100vh - 9rem)'}}}
|
||||||
>
|
>
|
||||||
|
@ -13,11 +13,11 @@ import {
|
|||||||
DatabaseLinkRegular,
|
DatabaseLinkRegular,
|
||||||
DeleteRegular,
|
DeleteRegular,
|
||||||
DismissRegular,
|
DismissRegular,
|
||||||
PlugConnectedRegular, PlugDisconnectedRegular,
|
PlugConnectedRegular,
|
||||||
SettingsRegular
|
SettingsRegular
|
||||||
} from "@fluentui/react-icons";
|
} from "@fluentui/react-icons";
|
||||||
import React, {useEffect, useState} from "react";
|
import React, { useEffect, useState} from "react";
|
||||||
import {Connection} from "../../interfaces/connection";
|
import { Connection} from "../../interfaces/connection";
|
||||||
import {useToast} from "../../message";
|
import {useToast} from "../../message";
|
||||||
import {Dial} from "../../api";
|
import {Dial} from "../../api";
|
||||||
import {useStoreConnection} from "../../store/connection";
|
import {useStoreConnection} from "../../store/connection";
|
||||||
@ -90,7 +90,7 @@ const useStyles = makeStyles({
|
|||||||
export function ConnectionList() {
|
export function ConnectionList() {
|
||||||
const styles = useStyles()
|
const styles = useStyles()
|
||||||
const {dispatchMessage} = useToast();
|
const {dispatchMessage} = useToast();
|
||||||
const {conn_get, conn_list, conn_set} = useStoreConnection();
|
const {conn_list, conn_update} = useStoreConnection();
|
||||||
const [conn_filter, set_conn_filter] = useState<string>('');
|
const [conn_filter, set_conn_filter] = useState<string>('');
|
||||||
const {bucket_get, bucket_set} = useStoreBucket()
|
const {bucket_get, bucket_set} = useStoreBucket()
|
||||||
const [ctx_menu, set_ctx_menu] = useState<{
|
const [ctx_menu, set_ctx_menu] = useState<{
|
||||||
@ -104,26 +104,22 @@ export function ConnectionList() {
|
|||||||
document.addEventListener("click", (e) => {
|
document.addEventListener("click", (e) => {
|
||||||
set_ctx_menu({x: 0, y: 0, display: 'none'});
|
set_ctx_menu({x: 0, y: 0, display: 'none'});
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
|
||||||
conn_get().then()
|
|
||||||
}, 1000)
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener("click", (e) => {
|
document.removeEventListener("click", (e) => {})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
async function handleSelect(item: Connection) {
|
async function handleSelect(item: Connection) {
|
||||||
conn_list.map((one: Connection) => {
|
conn_list.map((one: Connection) => {
|
||||||
if (item.id === one.id && one.active) {
|
if (item.id === one.id && one.active) {
|
||||||
conn_set(one)
|
conn_update(one)
|
||||||
bucket_get(one, false)
|
bucket_get(one, false)
|
||||||
bucket_set(null)
|
bucket_set(null)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleConnect(item: Connection | null) {
|
async function handleConnect(item: Connection|null) {
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
let res = await Dial('/api/connection/connect', {id: item.id});
|
let res = await Dial('/api/connection/connect', {id: item.id});
|
||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
@ -131,34 +127,28 @@ export function ConnectionList() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await conn_set({...item, active: true})
|
conn_update({...item, active: true})
|
||||||
bucket_get(item, true)
|
bucket_get(item, true)
|
||||||
await bucket_set(null)
|
bucket_set(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleDisconnect(item: Connection | null) {
|
async function handleDisconnect(item: Connection) {
|
||||||
if (!item) return;
|
|
||||||
let res = await Dial('/api/connection/disconnect', {id: item.id})
|
let res = await Dial('/api/connection/disconnect', {id: item.id})
|
||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
dispatchMessage(res.msg, "error")
|
dispatchMessage(res.msg, "error")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await conn_set({...item, active: false})
|
conn_update({...item, active: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleRightClick(e: React.MouseEvent<HTMLDivElement>, item: Connection) {
|
async function handleRightClick(e: React.MouseEvent<HTMLDivElement>, item: Connection) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
console.log('[DEBUG] right click connection =', item, 'event =', e)
|
||||||
|
console.log(`[DEBUG] click position: [${e.pageX}, ${e.pageY}]`)
|
||||||
set_menu_conn(item)
|
set_menu_conn(item)
|
||||||
|
|
||||||
const ele = document.querySelector('#list-connection-container')
|
|
||||||
const eleX = ele ? ele.clientWidth : 0;
|
|
||||||
const eleY = ele ? ele.clientHeight : 0;
|
|
||||||
const positionX = (e.pageX + eleX > window.innerWidth) ? e.pageX - eleX : e.pageX
|
|
||||||
const positionY = (e.pageY + eleY > window.innerHeight) ? e.pageY - eleY : e.pageY
|
|
||||||
|
|
||||||
set_ctx_menu({
|
set_ctx_menu({
|
||||||
x: positionX,
|
x: e.pageX,
|
||||||
y: positionY,
|
y: e.pageY,
|
||||||
display: 'block',
|
display: 'block',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -179,24 +169,15 @@ export function ConnectionList() {
|
|||||||
onChange={(e) => set_conn_filter(e.target.value)}
|
onChange={(e) => set_conn_filter(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className={styles.ctx_menu}
|
||||||
id={'list-connection-container'}
|
style={{left: ctx_menu.x, top: ctx_menu.y, display: ctx_menu.display}}
|
||||||
className={styles.ctx_menu}
|
|
||||||
style={{left: ctx_menu.x, top: ctx_menu.y, display: ctx_menu.display}}
|
|
||||||
>
|
>
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={async () => {
|
onClick={async () => {await handleConnect(menu_conn)}}
|
||||||
await handleConnect(menu_conn)
|
icon={<PlugConnectedRegular />}>连接</MenuItem>
|
||||||
}}
|
<MenuItem icon={<SettingsRegular />}>设置</MenuItem>
|
||||||
icon={<PlugConnectedRegular/>}>连接</MenuItem>
|
<MenuItem icon={<DeleteRegular />}>删除</MenuItem>
|
||||||
<MenuItem
|
|
||||||
onClick={async () => {
|
|
||||||
await handleDisconnect(menu_conn)
|
|
||||||
}}
|
|
||||||
icon={<PlugDisconnectedRegular/>}>断开</MenuItem>
|
|
||||||
<MenuItem icon={<SettingsRegular/>}>设置</MenuItem>
|
|
||||||
<MenuItem icon={<DeleteRegular/>}>删除</MenuItem>
|
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.items}>
|
<div className={styles.items}>
|
||||||
@ -216,6 +197,18 @@ export function ConnectionList() {
|
|||||||
icon={<DatabaseLinkRegular/>}
|
icon={<DatabaseLinkRegular/>}
|
||||||
key={item.id}>
|
key={item.id}>
|
||||||
{item.name}
|
{item.name}
|
||||||
|
<Tooltip
|
||||||
|
content="断开连接"
|
||||||
|
relationship="label">
|
||||||
|
<Button
|
||||||
|
appearance={'transparent'}
|
||||||
|
size="small"
|
||||||
|
icon={<DismissRegular/>}
|
||||||
|
className={styles.items_disconn}
|
||||||
|
onClick={async () => {
|
||||||
|
await handleDisconnect(item)
|
||||||
|
}}/>
|
||||||
|
</Tooltip>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
})}
|
})}
|
||||||
</MenuList>
|
</MenuList>
|
||||||
|
@ -69,7 +69,7 @@ export function ConnectionCreate(props: ConnectionCreateProps) {
|
|||||||
dispatchMessage(res.msg, res.status === 200 ? "success" : "error");
|
dispatchMessage(res.msg, res.status === 200 ? "success" : "error");
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
dispatchMessage("新建连接成功", "success");
|
dispatchMessage("新建连接成功", "success");
|
||||||
await conn_get()
|
conn_get()
|
||||||
props.openFn(false)
|
props.openFn(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import {ListBucketComponent} from "../bucket/list_bucket";
|
|||||||
import {makeStyles} from "@fluentui/react-components";
|
import {makeStyles} from "@fluentui/react-components";
|
||||||
import {useStoreBucket} from "../../store/bucket";
|
import {useStoreBucket} from "../../store/bucket";
|
||||||
import {ListFileComponent} from "./list_file";
|
import {ListFileComponent} from "./list_file";
|
||||||
import {useState} from "react";
|
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
content: {
|
content: {
|
||||||
@ -19,9 +18,8 @@ export function Content() {
|
|||||||
|
|
||||||
const styles = useStyles()
|
const styles = useStyles()
|
||||||
const {bucket_active } = useStoreBucket()
|
const {bucket_active } = useStoreBucket()
|
||||||
|
|
||||||
return <div className={styles.content}>
|
return <div className={styles.content}>
|
||||||
<Path />
|
<Path/>
|
||||||
{
|
{
|
||||||
bucket_active ?
|
bucket_active ?
|
||||||
<ListFileComponent/> :
|
<ListFileComponent/> :
|
||||||
|
@ -1,61 +1,25 @@
|
|||||||
import {makeStyles, MenuItem, MenuList, Spinner, Text, tokens} from "@fluentui/react-components";
|
import {makeStyles, MenuItem, MenuList, Text, tokens} from "@fluentui/react-components";
|
||||||
import {
|
import {
|
||||||
ArrowDownloadFilled,
|
|
||||||
DeleteRegular,
|
DocumentBulletListRegular, DocumentChevronDoubleRegular, DocumentCssRegular, DocumentDatabaseRegular,
|
||||||
DocumentBulletListRegular,
|
|
||||||
DocumentChevronDoubleRegular,
|
|
||||||
DocumentCssRegular,
|
|
||||||
DocumentDatabaseRegular,
|
|
||||||
DocumentDismissRegular,
|
DocumentDismissRegular,
|
||||||
DocumentImageRegular,
|
DocumentImageRegular, DocumentJavascriptRegular, DocumentPdfRegular, DocumentYmlRegular,
|
||||||
DocumentJavascriptRegular,
|
FolderRegular
|
||||||
DocumentPdfRegular,
|
|
||||||
DocumentYmlRegular,
|
|
||||||
FolderRegular,
|
|
||||||
PreviewLinkRegular
|
|
||||||
} from "@fluentui/react-icons";
|
} from "@fluentui/react-icons";
|
||||||
import {VirtualizerScrollView} from "@fluentui/react-components/unstable";
|
import {VirtualizerScrollView} from "@fluentui/react-components/unstable";
|
||||||
import React, {useEffect, useState} from "react";
|
import React from "react";
|
||||||
import {useStoreBucket} from "../../store/bucket";
|
import {useStoreBucket} from "../../store/bucket";
|
||||||
import {S3File} from "../../interfaces/connection";
|
import { S3File} from "../../interfaces/connection";
|
||||||
import {useStoreFile, useStoreFileFilter} from "../../store/file";
|
import {useStoreFile} from "../../store/file";
|
||||||
import {useStoreConnection} from "../../store/connection";
|
import {useStoreConnection} from "../../store/connection";
|
||||||
import {TrimSuffix} from "../../hook/strings";
|
import {TrimSuffix} from "../../hook/strings";
|
||||||
import {Dial} from "../../api";
|
|
||||||
import {useToast} from "../../message";
|
|
||||||
import {CanPreview} from "../../hook/preview";
|
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
container: {
|
container: {
|
||||||
marginTop: '0.5rem',
|
marginTop: '0.5rem',
|
||||||
maxWidth: 'calc(100vw - 25.2rem)',
|
maxWidth: 'calc(100vw - 25rem - 1px)',
|
||||||
width: 'calc(100vw - 25.2rem)',
|
width: 'calc(100vw - 25rem - 1px)',
|
||||||
maxHeight: 'calc(100vh - 10rem)',
|
height: 'calc(100vh - 9rem)',
|
||||||
height: 'calc(100vh - 10rem)',
|
|
||||||
},
|
|
||||||
loading: {
|
|
||||||
flex: "1",
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
flexDirection: 'column',
|
|
||||||
},
|
|
||||||
no_data: {
|
|
||||||
flex: "1",
|
|
||||||
height: '100%',
|
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
fontSize: '8rem',
|
|
||||||
flexDirection: 'column',
|
|
||||||
},
|
|
||||||
list: {
|
|
||||||
flex: "1",
|
|
||||||
height: '100%',
|
|
||||||
width: '100%',
|
|
||||||
},
|
},
|
||||||
row: {
|
row: {
|
||||||
height: '32px',
|
height: '32px',
|
||||||
@ -75,49 +39,24 @@ const useStyles = makeStyles({
|
|||||||
width: 'calc(100vw - 32rem)',
|
width: 'calc(100vw - 32rem)',
|
||||||
display: "block",
|
display: "block",
|
||||||
},
|
},
|
||||||
ctx_menu: {
|
no_data: {
|
||||||
position: "absolute",
|
flex: "1",
|
||||||
zIndex: "1000",
|
height: '100%',
|
||||||
width: "15rem",
|
width: '100%',
|
||||||
backgroundColor: tokens.colorNeutralBackground1,
|
display: 'flex',
|
||||||
boxShadow: `${tokens.shadow16}`,
|
justifyContent: 'center',
|
||||||
paddingTop: "4px",
|
alignItems: 'center',
|
||||||
paddingBottom: "4px",
|
fontSize: '8rem',
|
||||||
|
flexDirection: 'column',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
export function ListFileComponent() {
|
export function ListFileComponent() {
|
||||||
|
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
const {dispatchMessage} = useToast();
|
|
||||||
const {conn_active} = useStoreConnection();
|
const {conn_active} = useStoreConnection();
|
||||||
const {bucket_active} = useStoreBucket()
|
const {bucket_active} = useStoreBucket()
|
||||||
const {file_active, files_get, file_set, files_list} = useStoreFile()
|
const {files_get, files_list} = useStoreFile()
|
||||||
const {prefix, filter, prefix_set} = useStoreFileFilter()
|
|
||||||
const [ctx_menu, set_ctx_menu] = useState<{
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
display: 'none' | 'block'
|
|
||||||
}>({x: 0, y: 0, display: 'none'});
|
|
||||||
const [loading, set_loading] = useState(true)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
document.addEventListener("click", (e) => {
|
|
||||||
set_ctx_menu({x: 0, y: 0, display: 'none'});
|
|
||||||
})
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("click", (e) => {
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
set_loading(true)
|
|
||||||
files_get(conn_active!, bucket_active!, prefix, filter).then(() => {
|
|
||||||
// set_loading(false)
|
|
||||||
})
|
|
||||||
}, [conn_active, bucket_active, prefix, filter]);
|
|
||||||
|
|
||||||
const filename = (key: string) => {
|
const filename = (key: string) => {
|
||||||
let strs = TrimSuffix(key, "/").split("/")
|
let strs = TrimSuffix(key, "/").split("/")
|
||||||
@ -126,116 +65,50 @@ export function ListFileComponent() {
|
|||||||
|
|
||||||
async function handleClick(item: S3File) {
|
async function handleClick(item: S3File) {
|
||||||
if (item.type === 1) {
|
if (item.type === 1) {
|
||||||
await prefix_set(item.key)
|
files_get(conn_active!, bucket_active!, item.key)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleRightClick(e: React.MouseEvent<HTMLDivElement>, item: S3File) {
|
function handleRightClick(e: React.MouseEvent<HTMLDivElement>, item: S3File) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
await file_set(item.key)
|
|
||||||
const ele = document.querySelector('#list-file-container')
|
|
||||||
let eleX = ele ? ele.clientWidth : 0
|
|
||||||
let eleY = ele ? ele.clientHeight : 0
|
|
||||||
const positionX = (e.pageX + eleX > window.innerWidth) ? e.pageX - eleX : e.pageX
|
|
||||||
const positionY = (e.pageY + eleY > window.innerHeight) ? e.pageY - eleY : e.pageY
|
|
||||||
set_ctx_menu({
|
|
||||||
x: positionX,
|
|
||||||
y: positionY,
|
|
||||||
display: 'block',
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleDownload(file: string | null) {
|
return <MenuList className={styles.container}>
|
||||||
if (!file) return
|
{files_list.length ?
|
||||||
const res1 = await Dial<{ result: string }>("/runtime/dialog/save", {
|
<VirtualizerScrollView
|
||||||
default_filename: file,
|
numItems={files_list.length}
|
||||||
})
|
itemSize={32}
|
||||||
if (res1.status !== 200) {
|
container={{role: 'list', style: {maxHeight: 'calc(100vh - 9rem)'}}}
|
||||||
return
|
>
|
||||||
|
{(idx) => {
|
||||||
|
return <div
|
||||||
|
className={styles.row} key={idx}
|
||||||
|
onClick={async () => {
|
||||||
|
await handleClick(files_list[idx])
|
||||||
|
}}
|
||||||
|
onContextMenu={async (e) => {
|
||||||
|
handleRightClick(e, files_list[idx])
|
||||||
|
}}>
|
||||||
|
<MenuItem className={styles.item}
|
||||||
|
icon={files_list[idx].type ? <FolderRegular/> :
|
||||||
|
<FileIcon name={files_list[idx].name}/>}>
|
||||||
|
<Text truncate wrap={false} className={styles.text}>
|
||||||
|
{filename(files_list[idx].key)}
|
||||||
|
</Text>
|
||||||
|
</MenuItem>
|
||||||
|
</div>
|
||||||
|
}}
|
||||||
|
</VirtualizerScrollView> : <div className={styles.no_data}>
|
||||||
|
<div>
|
||||||
|
<DocumentDismissRegular/>
|
||||||
|
</div>
|
||||||
|
<Text size={900}>
|
||||||
|
没有文件
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
if (!res1.data) return
|
</MenuList>
|
||||||
const res2 = await Dial('/api/file/download', {
|
|
||||||
conn_id: conn_active?.id,
|
|
||||||
bucket: bucket_active?.name,
|
|
||||||
key: file,
|
|
||||||
location: res1.data.result,
|
|
||||||
})
|
|
||||||
if (res2.status === 200) {
|
|
||||||
dispatchMessage('保存文件成功', 'success')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handlePreview() {
|
|
||||||
dispatchMessage('todo', 'warning')
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className={styles.container}>
|
|
||||||
<div
|
|
||||||
id={'list-file-container'}
|
|
||||||
className={styles.ctx_menu}
|
|
||||||
style={{left: ctx_menu.x, top: ctx_menu.y, display: ctx_menu.display}}
|
|
||||||
>
|
|
||||||
<MenuList>
|
|
||||||
<MenuItem
|
|
||||||
onClick={async () => {
|
|
||||||
await handleDownload(file_active)
|
|
||||||
}}
|
|
||||||
icon={<ArrowDownloadFilled/>}>下载</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
disabled={!CanPreview(file_active ?? '')}
|
|
||||||
onClick={async () => {
|
|
||||||
await handlePreview()
|
|
||||||
}}
|
|
||||||
icon={<PreviewLinkRegular/>}>预览</MenuItem>
|
|
||||||
<MenuItem icon={<DeleteRegular/>}>删除</MenuItem>
|
|
||||||
</MenuList>
|
|
||||||
</div>
|
|
||||||
<div className={styles.loading} style={{display: loading ? 'flex' : 'none'}}>
|
|
||||||
<Spinner appearance="primary" label="加载中..."/>
|
|
||||||
</div>
|
|
||||||
{
|
|
||||||
// (!loading) && (files_list.length === 0) ?
|
|
||||||
// <div className={styles.no_data}>
|
|
||||||
// <div>
|
|
||||||
// <DocumentDismissRegular/>
|
|
||||||
// </div>
|
|
||||||
// <Text size={900}>
|
|
||||||
// 没有文件
|
|
||||||
// </Text>
|
|
||||||
// </div>
|
|
||||||
// : <></>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// (!loading && files_list) ?
|
|
||||||
// <VirtualizerScrollView
|
|
||||||
// numItems={files_list.length}
|
|
||||||
// itemSize={32}
|
|
||||||
// container={{role: 'list', style: {maxHeight: 'calc(100vh - 9rem)'}}}
|
|
||||||
// >
|
|
||||||
// {(idx) => {
|
|
||||||
// return <div
|
|
||||||
// className={styles.row} key={idx}
|
|
||||||
// onClick={async () => {
|
|
||||||
// await handleClick(files_list[idx])
|
|
||||||
// }}
|
|
||||||
// onContextMenu={async (e) => {
|
|
||||||
// await handleRightClick(e, files_list[idx])
|
|
||||||
// }}>
|
|
||||||
// <MenuItem className={styles.item}
|
|
||||||
// icon={files_list[idx].type ? <FolderRegular/> :
|
|
||||||
// <FileIcon name={files_list[idx].name}/>}>
|
|
||||||
// <Text truncate wrap={false} className={styles.text}>
|
|
||||||
// {filename(files_list[idx].key)}
|
|
||||||
// </Text>
|
|
||||||
// </MenuItem>
|
|
||||||
// </div>
|
|
||||||
// }}
|
|
||||||
// </VirtualizerScrollView> : <></>
|
|
||||||
}
|
|
||||||
<MenuList className={styles.list}>
|
|
||||||
</MenuList>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileIconProps = {
|
type FileIconProps = {
|
||||||
@ -263,7 +136,7 @@ function FileIcon(props: FileIconProps) {
|
|||||||
case "pdf":
|
case "pdf":
|
||||||
return <DocumentPdfRegular/>
|
return <DocumentPdfRegular/>
|
||||||
case "css":
|
case "css":
|
||||||
return <DocumentCssRegular/>
|
return <DocumentCssRegular />
|
||||||
case "js":
|
case "js":
|
||||||
return <DocumentJavascriptRegular/>
|
return <DocumentJavascriptRegular/>
|
||||||
case "yaml":
|
case "yaml":
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import {Button, Input, makeStyles, Text, tokens, Tooltip} from "@fluentui/react-components";
|
import {Button, Input, makeStyles, Text, tokens, Tooltip} from "@fluentui/react-components";
|
||||||
import {useStoreBucket} from "../../store/bucket";
|
import {useStoreBucket} from "../../store/bucket";
|
||||||
import {ArchiveRegular, ArrowCurveUpLeftFilled} from "@fluentui/react-icons";
|
import {ArchiveRegular, ArrowCurveUpLeftFilled} from "@fluentui/react-icons";
|
||||||
import {useStoreFile, useStoreFileFilter} from "../../store/file";
|
import {useStoreFile} from "../../store/file";
|
||||||
import React, {useState} from "react";
|
import React from "react";
|
||||||
import {debounce} from 'lodash'
|
import {debounce} from 'lodash'
|
||||||
import {useStoreConnection} from "../../store/connection";
|
import {useStoreConnection} from "../../store/connection";
|
||||||
import {ListFileComponent} from "./list_file";
|
|
||||||
import {ListBucketComponent} from "../bucket/list_bucket";
|
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
container: {
|
container: {
|
||||||
@ -53,28 +51,27 @@ const useStyles = makeStyles({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
export function Path() {
|
export function Path() {
|
||||||
const styles = useStyles()
|
const styles = useStyles()
|
||||||
const {conn_active} = useStoreConnection()
|
const {conn_active} = useStoreConnection()
|
||||||
const {bucket_active, bucket_get, bucket_set} = useStoreBucket()
|
const {bucket_active, bucket_get, bucket_set} = useStoreBucket()
|
||||||
const {prefix, filter, prefix_set, filter_set} = useStoreFileFilter()
|
const {prefix, files_get} = useStoreFile()
|
||||||
|
|
||||||
async function handleClickUp() {
|
async function handleClickUp() {
|
||||||
const dirs = prefix.split('/').filter((item => item))
|
const dirs = prefix.split('/').filter((item => item))
|
||||||
if (dirs.length > 0) {
|
if (dirs.length > 0) {
|
||||||
dirs.pop()
|
dirs.pop()
|
||||||
await prefix_set(dirs.join('/'))
|
files_get(conn_active!, bucket_active!, dirs.join("/"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bucket_get(conn_active!, false)
|
bucket_get(conn_active!, false)
|
||||||
await bucket_set(null)
|
bucket_set(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const handleFilterChange = debounce(async (e) => {
|
const handleFilterChange = debounce((e) => {
|
||||||
await filter_set(e.target.value)
|
files_get(conn_active!, bucket_active!, prefix, e.target.value)
|
||||||
}, 500)
|
}, 500)
|
||||||
|
|
||||||
return <div className={styles.container}>
|
return <div className={styles.container}>
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
import {makeStyles} from "@fluentui/react-components";
|
import {Button, Input, makeStyles, MenuItem, MenuList, mergeClasses, tokens, Tooltip} from "@fluentui/react-components";
|
||||||
|
import {DismissRegular} from "@fluentui/react-icons";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import {Connection} from "../../interfaces/connection";
|
||||||
|
import {useStoreBucket} from "../../store/bucket";
|
||||||
|
import {useStoreConnection} from "../../store/connection";
|
||||||
|
import {Dial} from "../../api";
|
||||||
|
import {useToast} from "../../message";
|
||||||
import {ConnectionList} from "../connection/list";
|
import {ConnectionList} from "../connection/list";
|
||||||
import {Content} from "../file/content";
|
import {Content} from "../file/content";
|
||||||
|
|
||||||
@ -13,9 +20,15 @@ const useStyles = makeStyles({
|
|||||||
|
|
||||||
export function Body() {
|
export function Body() {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
const {conn_get} = useStoreConnection();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
conn_get()
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
return <div className={styles.body}>
|
return <div className={styles.body}>
|
||||||
<ConnectionList/>
|
<ConnectionList/>
|
||||||
<Content/>
|
<Content />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import {Button, Dialog, DialogTrigger, makeStyles} from "@fluentui/react-components";
|
import {Button, Dialog, DialogTrigger, makeStyles} from "@fluentui/react-components";
|
||||||
import {ConnectionCreate} from "../connection/new";
|
import {ConnectionCreate} from "../connection/new";
|
||||||
import {AppsAddInRegular, DocumentArrowUpRegular, PlugConnectedAddRegular} from "@fluentui/react-icons";
|
import {AppsAddInRegular, DocumentArrowUpRegular, PlugConnectedAddRegular} from "@fluentui/react-icons";
|
||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import {useStoreConnection} from "../../store/connection";
|
import {useStoreConnection} from "../../store/connection";
|
||||||
import {BucketCreate} from "../bucket/new";
|
import {BucketCreate} from "../bucket/new";
|
||||||
@ -10,7 +10,6 @@ import {UploadFiles} from "../file/upload_files";
|
|||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
header: {
|
header: {
|
||||||
height: "5rem",
|
height: "5rem",
|
||||||
minHeight: '5rem',
|
|
||||||
width: "100%",
|
width: "100%",
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
@ -65,7 +64,7 @@ export function Header() {
|
|||||||
open={open_upload}
|
open={open_upload}
|
||||||
onOpenChange={(event, data) => set_open_upload(data.open)}>
|
onOpenChange={(event, data) => set_open_upload(data.open)}>
|
||||||
<DialogTrigger disableButtonEnhancement>
|
<DialogTrigger disableButtonEnhancement>
|
||||||
<Button appearance="primary" icon={<DocumentArrowUpRegular/>}>
|
<Button appearance="primary" icon={<DocumentArrowUpRegular />}>
|
||||||
上传
|
上传
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
export function CanPreview(filename: string) {
|
|
||||||
const fs = filename.split(".")
|
|
||||||
switch (fs[fs.length - 1]) {
|
|
||||||
case "jpg":
|
|
||||||
return "image/jpg"
|
|
||||||
case "jpeg":
|
|
||||||
return "image/jpg"
|
|
||||||
case "png":
|
|
||||||
return "image/png"
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ import {Dial, Resp} from "../api";
|
|||||||
|
|
||||||
interface StoreBucket {
|
interface StoreBucket {
|
||||||
bucket_active: Bucket | null;
|
bucket_active: Bucket | null;
|
||||||
bucket_set: (Bucket: Bucket | null) => Promise<void>;
|
bucket_set: (Bucket: Bucket | null) => void;
|
||||||
bucket_list: Bucket[];
|
bucket_list: Bucket[];
|
||||||
bucket_get: (conn: Connection, refresh: boolean) => void;
|
bucket_get: (conn: Connection, refresh: boolean) => void;
|
||||||
bucket_create: (conn: Connection, name: string, public_read: boolean, public_read_write: boolean) => void;
|
bucket_create: (conn: Connection, name: string, public_read: boolean, public_read_write: boolean) => void;
|
||||||
|
@ -5,8 +5,8 @@ import {Dial} from "../api";
|
|||||||
interface StoreConnection {
|
interface StoreConnection {
|
||||||
conn_active: Connection | null;
|
conn_active: Connection | null;
|
||||||
conn_list: Connection[];
|
conn_list: Connection[];
|
||||||
conn_get: () => Promise<void>;
|
conn_get: () => void;
|
||||||
conn_set: (connection: Connection) => Promise<void>;
|
conn_update: (connection: Connection) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useStoreConnection = create<StoreConnection>()((set) => ({
|
export const useStoreConnection = create<StoreConnection>()((set) => ({
|
||||||
@ -21,7 +21,7 @@ export const useStoreConnection = create<StoreConnection>()((set) => ({
|
|||||||
set({conn_list: res.data.list})
|
set({conn_list: res.data.list})
|
||||||
},
|
},
|
||||||
|
|
||||||
conn_set: async (connection: Connection) => {
|
conn_update: async (connection: Connection) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
return {
|
return {
|
||||||
conn_active: connection.active? connection: null,
|
conn_active: connection.active? connection: null,
|
||||||
|
@ -3,19 +3,15 @@ import {Bucket, Connection, S3File} from "../interfaces/connection";
|
|||||||
import {Dial} from "../api";
|
import {Dial} from "../api";
|
||||||
|
|
||||||
interface StoreFile {
|
interface StoreFile {
|
||||||
file_active: string | null,
|
prefix: string;
|
||||||
file_set: (key: string) => Promise<void>,
|
filter:string;
|
||||||
files_list: S3File[];
|
files_list: S3File[];
|
||||||
files_get: (conn: Connection, bucket: Bucket, prefix?: string, filter?: string) => Promise<void>;
|
files_get: (conn: Connection, bucket: Bucket, prefix?: string, filter?: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useStoreFile = create<StoreFile>()((set) => ({
|
export const useStoreFile = create<StoreFile>()((set) => ({
|
||||||
file_active: null,
|
prefix: "",
|
||||||
file_set: async (key: string) => {
|
filter: "",
|
||||||
set((state) => {
|
|
||||||
return {file_active: key}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
files_list: [],
|
files_list: [],
|
||||||
files_get: async (conn: Connection, bucket: Bucket, prefix = '', filter = '') => {
|
files_get: async (conn: Connection, bucket: Bucket, prefix = '', filter = '') => {
|
||||||
const res = await Dial<{ list: S3File[] }>('/api/bucket/files', {
|
const res = await Dial<{ list: S3File[] }>('/api/bucket/files', {
|
||||||
@ -29,25 +25,7 @@ export const useStoreFile = create<StoreFile>()((set) => ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
return {files_list: res.data.list}
|
return {files_list: res.data.list, prefix: prefix, filter: filter}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
}))
|
|
||||||
|
|
||||||
interface StoreFileFilter {
|
|
||||||
prefix: string;
|
|
||||||
filter: string;
|
|
||||||
prefix_set: (prefix: string) => Promise<void>;
|
|
||||||
filter_set: (filter: string) => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useStoreFileFilter = create<StoreFileFilter>()((set) => ({
|
|
||||||
prefix: '',
|
|
||||||
filter: '',
|
|
||||||
prefix_set: async (keyword: string) => {
|
|
||||||
set(state => {return {prefix: keyword}})
|
|
||||||
},
|
|
||||||
filter_set: async (keyword: string) => {
|
|
||||||
set(state => {return {filter: keyword}})
|
|
||||||
},
|
|
||||||
}))
|
}))
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import {create} from 'zustand'
|
import {create} from 'zustand'
|
||||||
import {Bucket, Connection} from "../interfaces/connection";
|
import {Bucket, Connection} from "../interfaces/connection";
|
||||||
|
|
||||||
@ -5,14 +6,13 @@ interface StorePreview {
|
|||||||
preview_key: string;
|
preview_key: string;
|
||||||
preview_url: string;
|
preview_url: string;
|
||||||
preview_content_type: string;
|
preview_content_type: string;
|
||||||
preview_set: (key: string) => void;
|
preview_set: (conn:Connection,bucket: Bucket,key: string) => void;
|
||||||
}
|
}
|
||||||
|
export const useStoreFile = create<StorePreview>()((set) => ({
|
||||||
export const useStorePreview = create<StorePreview>()((set) => ({
|
|
||||||
preview_key: '',
|
preview_key: '',
|
||||||
preview_url: '',
|
preview_url: '',
|
||||||
preview_content_type: '',
|
preview_content_type: '',
|
||||||
preview_set: async (key: string) => set(state => {
|
preview_set: (conn:Connection,bucket: Bucket,key: string) => set(state => {
|
||||||
return {preview_key: key}
|
return {preview_key: key}
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
2
go.mod
2
go.mod
@ -10,7 +10,6 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.36
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.36
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.63.2
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.63.2
|
||||||
github.com/aws/smithy-go v1.21.0
|
github.com/aws/smithy-go v1.21.0
|
||||||
github.com/labstack/gommon v0.4.0
|
|
||||||
github.com/loveuer/go-sqlite3 v1.0.2
|
github.com/loveuer/go-sqlite3 v1.0.2
|
||||||
github.com/loveuer/nf v0.2.11
|
github.com/loveuer/nf v0.2.11
|
||||||
github.com/ncruces/go-sqlite3/gormlite v0.18.4
|
github.com/ncruces/go-sqlite3/gormlite v0.18.4
|
||||||
@ -50,6 +49,7 @@ require (
|
|||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/labstack/echo/v4 v4.10.2 // indirect
|
github.com/labstack/echo/v4 v4.10.2 // indirect
|
||||||
|
github.com/labstack/gommon v0.4.0 // indirect
|
||||||
github.com/leaanthony/go-ansi-parser v1.6.0 // indirect
|
github.com/leaanthony/go-ansi-parser v1.6.0 // indirect
|
||||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||||
github.com/leaanthony/slicer v1.6.0 // indirect
|
github.com/leaanthony/slicer v1.6.0 // indirect
|
||||||
|
@ -25,7 +25,6 @@ func Resolve(path string) (ndh.Handler, bool) {
|
|||||||
|
|
||||||
func Init(ctx context.Context) error {
|
func Init(ctx context.Context) error {
|
||||||
register("/runtime/dialog/open", handler.DialogOpen(ctx))
|
register("/runtime/dialog/open", handler.DialogOpen(ctx))
|
||||||
register("/runtime/dialog/save", handler.DialogSave(ctx))
|
|
||||||
register("/api/connection/test", handler.ConnectionTest)
|
register("/api/connection/test", handler.ConnectionTest)
|
||||||
register("/api/connection/create", handler.ConnectionCreate)
|
register("/api/connection/create", handler.ConnectionCreate)
|
||||||
register("/api/connection/list", handler.ConnectionList)
|
register("/api/connection/list", handler.ConnectionList)
|
||||||
@ -37,7 +36,6 @@ func Init(ctx context.Context) error {
|
|||||||
register("/api/file/upload", handler.FileUpload)
|
register("/api/file/upload", handler.FileUpload)
|
||||||
register("/api/file/info", handler.FileInfo)
|
register("/api/file/info", handler.FileInfo)
|
||||||
register("/api/file/get", handler.FileGet)
|
register("/api/file/get", handler.FileGet)
|
||||||
register("/api/file/download", handler.FileDownload)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/loveuer/nf-disk/internal/s3"
|
"github.com/loveuer/nf-disk/internal/s3"
|
||||||
"github.com/loveuer/nf-disk/ndh"
|
"github.com/loveuer/nf-disk/ndh"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ConnectionTest(c *ndh.Ctx) error {
|
func ConnectionTest(c *ndh.Ctx) error {
|
||||||
@ -209,5 +210,11 @@ func ConnectionBuckets(c *ndh.Ctx) error {
|
|||||||
return c.Send500(err.Error())
|
return c.Send500(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: for frontend test
|
||||||
|
buckets = append(buckets, &s3.ListBucketRes{
|
||||||
|
Name: "这是一个非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长非常长的名字",
|
||||||
|
CreatedAt: time.Now().UnixMilli(),
|
||||||
|
})
|
||||||
|
|
||||||
return c.Send200(map[string]any{"list": buckets})
|
return c.Send200(map[string]any{"list": buckets})
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,6 @@ import (
|
|||||||
"github.com/loveuer/nf-disk/ndh"
|
"github.com/loveuer/nf-disk/ndh"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func DialogOpen(ctx context.Context) ndh.Handler {
|
func DialogOpen(ctx context.Context) ndh.Handler {
|
||||||
@ -56,58 +54,3 @@ func DialogOpen(ctx context.Context) ndh.Handler {
|
|||||||
return c.Send200(map[string]interface{}{"result": result})
|
return c.Send200(map[string]interface{}{"result": result})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DialogSave(ctx context.Context) ndh.Handler {
|
|
||||||
return func(c *ndh.Ctx) error {
|
|
||||||
type Req struct {
|
|
||||||
Title string `json:"title"`
|
|
||||||
Filters []string `json:"filters"`
|
|
||||||
DefaultDirectory string `json:"default_directory"`
|
|
||||||
DefaultFilename string `json:"default_filename"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
req = new(Req)
|
|
||||||
opt = runtime.SaveDialogOptions{
|
|
||||||
Title: "将文件保存到",
|
|
||||||
}
|
|
||||||
result any
|
|
||||||
)
|
|
||||||
|
|
||||||
if err = c.ReqParse(req); err != nil {
|
|
||||||
return c.Send400(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Title != "" {
|
|
||||||
opt.Title = req.Title
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.DefaultFilename != "" {
|
|
||||||
opt.DefaultFilename = req.DefaultFilename
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.DefaultDirectory != "" {
|
|
||||||
opt.DefaultDirectory = req.DefaultDirectory
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.DefaultDirectory == "" {
|
|
||||||
var home string
|
|
||||||
if home, err = os.UserHomeDir(); err != nil {
|
|
||||||
opt.DefaultDirectory = filepath.Join(home, "Downloads")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(req.Filters) > 0 {
|
|
||||||
opt.Filters = lo.Map(req.Filters, func(item string, index int) runtime.FileFilter {
|
|
||||||
return runtime.FileFilter{Pattern: item}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if result, err = runtime.SaveFileDialog(ctx, opt); err != nil {
|
|
||||||
return c.Send500(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Send200(map[string]interface{}{"result": result})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -138,48 +138,9 @@ func FileGet(c *ndh.Ctx) error {
|
|||||||
return c.Send500(err.Error())
|
return c.Send500(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if link, err = client.GetObjectEntry(c.Context(), req.Bucket, req.Key); err != nil {
|
if link, err = client.GetObject(c.Context(), req.Bucket, req.Key); err != nil {
|
||||||
return c.Send500(err.Error())
|
return c.Send500(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Send200(link)
|
return c.Send200(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FileDownload(c *ndh.Ctx) error {
|
|
||||||
type Req struct {
|
|
||||||
ConnId uint64 `json:"conn_id"`
|
|
||||||
Bucket string `json:"bucket"`
|
|
||||||
Key string `json:"key"`
|
|
||||||
Location string `json:"location"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
req = new(Req)
|
|
||||||
client *s3.Client
|
|
||||||
obj *s3.ObjectEntity
|
|
||||||
target *os.File
|
|
||||||
)
|
|
||||||
|
|
||||||
if err = c.ReqParse(req); err != nil {
|
|
||||||
return c.Send400(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, client, err = manager.Manager.Use(req.ConnId); err != nil {
|
|
||||||
return c.Send500(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if obj, err = client.GetObject(c.Context(), req.Bucket, req.Key); err != nil {
|
|
||||||
return c.Send500(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if target, err = os.OpenFile(filepath.Clean(req.Location), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644); err != nil {
|
|
||||||
return c.Send500(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = io.Copy(target, obj.Body); err != nil {
|
|
||||||
return c.Send500(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Send200(req)
|
|
||||||
}
|
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
|
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
"github.com/labstack/gommon/log"
|
"github.com/labstack/gommon/log"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -67,7 +66,7 @@ type ObjectEntry struct {
|
|||||||
Header http.Header
|
Header http.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetObjectEntry(ctx context.Context, bucket string, key string, lifetimes ...int64) (*ObjectEntry, error) {
|
func (c *Client) GetObject(ctx context.Context, bucket string, key string, lifetimes ...int64) (*ObjectEntry, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
lifetime int64 = 5 * 60
|
lifetime int64 = 5 * 60
|
||||||
@ -89,33 +88,3 @@ func (c *Client) GetObjectEntry(ctx context.Context, bucket string, key string,
|
|||||||
Header: output.SignedHeader,
|
Header: output.SignedHeader,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ObjectEntity struct {
|
|
||||||
ObjectInfo
|
|
||||||
Body io.ReadCloser
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) GetObject(ctx context.Context, bucket string, key string) (*ObjectEntity, error) {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
input = &s3.GetObjectInput{
|
|
||||||
Bucket: aws.String(bucket),
|
|
||||||
Key: aws.String(key),
|
|
||||||
}
|
|
||||||
output *s3.GetObjectOutput
|
|
||||||
)
|
|
||||||
|
|
||||||
if output, err = c.client.GetObject(ctx, input); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ObjectEntity{
|
|
||||||
ObjectInfo: ObjectInfo{
|
|
||||||
Bucket: bucket,
|
|
||||||
Key: key,
|
|
||||||
ContentType: aws.ToString(output.ContentType),
|
|
||||||
Expire: aws.ToTime(output.Expires).UnixMilli(),
|
|
||||||
},
|
|
||||||
Body: output.Body,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
10
xtest/path.js
Normal file
10
xtest/path.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
function getBaseFileName(fullPath) {
|
||||||
|
return fullPath.replace(/.*[\/\\]/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试
|
||||||
|
const filePath = 'C:\\Users\\username\\Documents\\example.txt';
|
||||||
|
console.log(getBaseFileName(filePath)); // 输出: example.txt
|
||||||
|
|
||||||
|
const filePath2 = '/home/user/documents/example.txt';
|
||||||
|
console.log(getBaseFileName(filePath2)); // 输出: example.txt
|
Loading…
x
Reference in New Issue
Block a user