Skip to content

Commit

Permalink
Fix upload
Browse files Browse the repository at this point in the history
  • Loading branch information
ShirasawaSama committed Jul 31, 2021
1 parent 73373a3 commit f988d22
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 39 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nekomaid",
"version": "0.0.4",
"version": "0.0.6",
"description": "A plugin can use Web to manage your server.",
"main": "index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/cn/apisium/nekomaid/NekoMaid.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
import java.util.function.*;

@SuppressWarnings({"UnusedReturnValue", "unused"})
@Plugin(name = "NekoMaid", version = "0.0.4")
@Plugin(name = "NekoMaid", version = "0.0.0")
@Description("A plugin can use Web to manage your server.")
@Author("Shirasawa")
@Website("https://neko-craft.com")
Expand Down
59 changes: 30 additions & 29 deletions src/main/java/cn/apisium/nekomaid/builtin/FilesManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@
final class FilesManager {
private static final ArchiveStreamFactory archiveFactory = new ArchiveStreamFactory();
private static final DefaultHttpDataFactory factory = new DefaultHttpDataFactory();
private final static int UPLOAD_STARTS = "/NekoMaidUpload/".length();
private final static int DOWNLOAD_STARTS = "/NekoMaidDownload/".length();
private final static long MAX_SIZE = 4 * 1024 * 1024; // 4MB
private final static Path root = Paths.get(".");
private final Cache<String, Path> uploadMap = createCache();
Expand Down Expand Up @@ -222,18 +220,18 @@ protected void channelRead0(ChannelHandlerContext ctx, HttpContent msg) throws E
private final class UploadHandler implements UniporterHttpHandler {
@Override
public void hijack(ChannelHandlerContext context, HttpRequest request) {
context.pipeline().remove(Constants.PRE_ROUTE_ID);
if (HttpMethod.PUT == request.method() && request.uri().length() > UPLOAD_STARTS) {
Path p = uploadMap.getIfPresent(request.uri().substring(UPLOAD_STARTS));
if (p != null) context.pipeline().replace(Constants.AGGREGATOR_HANDLER_ID, "UploadDataHandler",
new UploadDataHandler(request, p.toFile()));
}
if (HttpMethod.PUT != request.method()) return;
String[] arr = request.uri().split("/");
if (arr.length == 0) return;
Path p = uploadMap.getIfPresent(arr[arr.length - 1]);
if (p != null) context.pipeline().replace(Constants.AGGREGATOR_HANDLER_ID, "UploadDataHandler",
new UploadDataHandler(request, p.toFile()));
}

@Override
public void handle(String path, Route route, ChannelHandlerContext context, FullHttpRequest request) {
HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
request.method() == HttpMethod.OPTIONS ? HttpResponseStatus.OK : HttpResponseStatus.FORBIDDEN);
request.method() == HttpMethod.OPTIONS ? HttpResponseStatus.OK : HttpResponseStatus.METHOD_NOT_ALLOWED);
addHeaders(request, response);
context.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
Expand All @@ -245,26 +243,29 @@ public void handle(String path, Route route, ChannelHandlerContext context, Full
private final class DownloadHandler implements UniporterHttpHandler {
@Override
public void handle(String path, Route route, ChannelHandlerContext context, FullHttpRequest request) {
if (request.method() == HttpMethod.GET && path.length() > DOWNLOAD_STARTS) {
Path file = downloadMap.getIfPresent(path.substring(DOWNLOAD_STARTS));
if (file != null) {
try {
if (context.pipeline().get(HttpContentCompressor.class) != null)
context.pipeline().remove(HttpContentCompressor.class);
RandomAccessFile raf = new RandomAccessFile(file.toFile().getAbsolutePath(), "r");
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
long length = raf.length();
HttpUtil.setContentLength(response, length);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_OCTET_STREAM)
.set(HttpHeaderNames.CONTENT_DISPOSITION, "attachment; filename=" + file.getFileName().toString());
context.write(response);
context.write(context.pipeline().get(SslHandler.class) == null
? new DefaultFileRegion(raf.getChannel(), 0, length)
: new ChunkedFile(raf), context.newProgressivePromise());
context.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE);
return;
} catch (Throwable e) {
e.printStackTrace();
if (request.method() == HttpMethod.GET) {
String[] arr = request.uri().split("/");
if (arr.length != 0) {
Path file = downloadMap.getIfPresent(arr[arr.length - 1]);
if (file != null) {
try {
if (context.pipeline().get(HttpContentCompressor.class) != null)
context.pipeline().remove(HttpContentCompressor.class);
RandomAccessFile raf = new RandomAccessFile(file.toFile().getAbsolutePath(), "r");
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
long length = raf.length();
HttpUtil.setContentLength(response, length);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_OCTET_STREAM)
.set(HttpHeaderNames.CONTENT_DISPOSITION, "attachment; filename=" + file.getFileName().toString());
context.write(response);
context.write(context.pipeline().get(SslHandler.class) == null
? new DefaultFileRegion(raf.getChannel(), 0, length)
: new ChunkedFile(raf), context.newProgressivePromise());
context.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE);
return;
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}
Expand Down
30 changes: 24 additions & 6 deletions web/pages/Console.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React, { useMemo, useEffect, useState, useRef } from 'react'
import React, { useMemo, useEffect, useState, useRef, createRef } from 'react'
import { usePlugin } from '../Context'
import { Send } from '@material-ui/icons'
import { TextField, Toolbar, IconButton, Paper, Tooltip, Box, Autocomplete } from '@material-ui/core'
import { parseComponents, parseMessage, TextComponent } from '../utils'
import { address } from '../url'
import throttle from 'lodash/throttle'
import toast from '../toast'
import toast, { success } from '../toast'
import More from '../components/More'
import dialog from '../dialog'

type Log = { level: string, msg: string, time: number, logger: string, components?: TextComponent[] }

Expand All @@ -25,20 +26,36 @@ const pad = (it: number) => it.toString().padStart(2, '0')

const parseLog = (data: Log, runCommand: (it: string) => void, suggest: (it: string) => void) => {
const t = new Date(data.time)
const ref = createRef<HTMLParagraphElement>()
const onShare = () => {
if (!ref.current) return
const text = ref.current.textContent || ref.current.innerText
dialog(<><span className='bold'>确认要分享这段日志吗:</span><br />{text.slice(5, 150)}...</>).then(res => {
if (!res) return
toast('分享中...')
const body = new FormData()
body.set('content', text)
fetch('https://api.mclo.gs/1/log', { method: 'POST', body }).then(it => it.json()).then(it => {
if (!it.success) throw new Error('Failed!')
success()
window.open(it.url, '_blank')
})
})
}
const time = pad(t.getHours()) + ':' + pad(t.getMinutes()) + ':' + pad(t.getSeconds())
let moreLines = false
if (data.components) {
return <p key={i}>
<Tooltip title={time} placement='right'><span className='level'>[信息] </span></Tooltip>
return <p ref={ref} key={i}>
<Tooltip title={time} placement='right'><span className='level' onClick={onShare}>[信息] </span></Tooltip>
<span className='msg'>{parseComponents(data.components, runCommand, suggest)}</span>
</p>
} else {
const msg = parseMessage(data.msg)
const isError = data.level === 'FATAL' || data.level === 'ERROR'
moreLines = (isError || data.level === 'WARN') && data.msg.includes('\n')
const elm = <p key={i} className={isError ? 'error' : data.level === 'WARN' ? 'warn' : undefined}>
const elm = <p ref={ref} key={i} className={isError ? 'error' : data.level === 'WARN' ? 'warn' : undefined}>
<Tooltip title={time} placement='right'>
<span className='level'>[{levelNames[data.level] || '信息'}] </span>
<span className='level' onClick={onShare}>[{levelNames[data.level] || '信息'}] </span>
</Tooltip>
<span className='msg'>
{moreLines && <span className='more' data-collapse='[收起]'>[展开]</span>}
Expand Down Expand Up @@ -146,6 +163,7 @@ const Console: React.FC = () => {
userSelect: 'none',
height: 'fit-content',
fontWeight: 'bolder',
cursor: 'pointer',
color: theme => theme.palette.primary.main
},
'& .white': {
Expand Down
5 changes: 3 additions & 2 deletions web/pages/Files.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -448,10 +448,11 @@ const Files: React.FC = () => {
formdata.append('file', file)
const xhr = new XMLHttpRequest()
setProgress(0)
xhr.open('post', address! + 'Upload/' + res)
xhr.open('put', address! + 'Upload/' + res)
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return
setProgress(-1)
action(xhr.readyState === 4 && xhr.status === 200)
action(xhr.status === 200)
refresh()
}
xhr.upload.onprogress = e => e.lengthComputable && setProgress(e.loaded / e.total * 100)
Expand Down

0 comments on commit f988d22

Please sign in to comment.