Add stats route (WIP) + add /web/ testing route (WIP)
This commit is contained in:
parent
34851a7078
commit
b720b5f0d6
|
@ -1,10 +1,10 @@
|
|||
/** @jsx jsx */
|
||||
import { AppBar, Button, Card, CardContent, Container, Input, Tab, Tabs, Typography } from "@material-ui/core"
|
||||
import * as R from "ramda"
|
||||
import { useState } from "react"
|
||||
import { Box, Flex, jsx } from "theme-ui"
|
||||
import Dropzone from "../src/components/Dropzone"
|
||||
import { TabPanel } from "../src/components/TabPanel"
|
||||
import { AppBar, Button, Card, CardContent, Container, Input, Tab, Tabs, Typography } from "@material-ui/core";
|
||||
import * as R from "ramda";
|
||||
import { useState } from "react";
|
||||
import { Box, Flex, jsx } from "theme-ui";
|
||||
import Dropzone from "../src/components/Dropzone";
|
||||
import { TabPanel } from "../src/components/TabPanel";
|
||||
|
||||
const Index = () => {
|
||||
const [value, setValue] = useState(1)
|
||||
|
@ -31,6 +31,9 @@ const Index = () => {
|
|||
<Typography sx={{ fontWeight: 700 }}>Sia Skynet</Typography>
|
||||
</Box>
|
||||
<Box sx={{ ml: "auto" }}>
|
||||
<Button href="/stats">
|
||||
Statistics
|
||||
</Button>
|
||||
<Button href="https://sia.tech/" target="_blank">
|
||||
About Sia
|
||||
</Button>
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/** @jsx jsx */
|
||||
import { Button, CircularProgress, Container, Typography } from "@material-ui/core";
|
||||
import axios from "axios";
|
||||
import { useState } from "react";
|
||||
import { Box, Flex, jsx } from "theme-ui";
|
||||
|
||||
const API_ENDPOINT = process.env.NODE_ENV === "development" ? "http://localhost:4000" : "/api"
|
||||
|
||||
type PSeries = {
|
||||
'p80-1': number,
|
||||
'p95-1': number,
|
||||
'p99-1': number,
|
||||
'p80-24': number,
|
||||
'p95-24': number,
|
||||
'p99-24': number,
|
||||
'p80-168': number,
|
||||
'p95-168': number,
|
||||
'p99-168': number,
|
||||
}
|
||||
|
||||
type TSeries = {
|
||||
1: number,
|
||||
24: number,
|
||||
168: number,
|
||||
720?: number,
|
||||
2160?: number,
|
||||
}
|
||||
|
||||
type Stats = {
|
||||
'ttfb': PSeries,
|
||||
'download_small': PSeries,
|
||||
'download_throughput': PSeries,
|
||||
'download_total': TSeries,
|
||||
'upload_small': PSeries,
|
||||
'upload_throughput': PSeries,
|
||||
'upload_total': TSeries,
|
||||
'money_spent': TSeries,
|
||||
'total_files_pinned': number,
|
||||
'total_files_served': TSeries,
|
||||
}
|
||||
|
||||
const Stats = () => {
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [stats, setStats] = useState<Stats>()
|
||||
|
||||
if (loading) {
|
||||
axios.get(`${API_ENDPOINT}/stats`)
|
||||
.then((response: any) => {
|
||||
console.log(response)
|
||||
setLoading(false)
|
||||
setStats(response.data as Stats)
|
||||
}).catch(e => {
|
||||
console.log('ERROR:', e)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box color="white">
|
||||
<Container>
|
||||
<Flex sx={{ alignItems: "center", height: 120 }}>
|
||||
<Box>
|
||||
<Typography sx={{ fontWeight: 700 }}>Sia Skynet</Typography>
|
||||
</Box>
|
||||
<Box sx={{ ml: "auto" }}>
|
||||
<Button href="/stats">
|
||||
Statistics
|
||||
</Button>
|
||||
<Button href="https://sia.tech/" target="_blank">
|
||||
About Sia
|
||||
</Button>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Container>
|
||||
</Box>
|
||||
<Box>
|
||||
<Container>
|
||||
<h2>Portal Statistics</h2>
|
||||
|
||||
<Flex
|
||||
sx={{ height: 400, justifyContent: "center", alignItems: "center" }}
|
||||
>
|
||||
{loading && <CircularProgress />}
|
||||
{!loading &&
|
||||
JSON.stringify(stats)
|
||||
}
|
||||
</Flex>
|
||||
</Container>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Stats
|
|
@ -9,8 +9,18 @@ const logger = createLogger({
|
|||
),
|
||||
transports: [
|
||||
new transports.Console(),
|
||||
new transports.File({ filename: './error.log', level: 'error' }),
|
||||
new transports.File({ filename: './info.log', level: 'info' }),
|
||||
new transports.File({
|
||||
filename: './error.log',
|
||||
level: 'error',
|
||||
maxsize: 5242880,
|
||||
maxFiles: 2
|
||||
}),
|
||||
new transports.File({
|
||||
filename: './info.log',
|
||||
level: 'info',
|
||||
maxsize: 5242880,
|
||||
maxFiles: 5
|
||||
}),
|
||||
],
|
||||
exitOnError: false,
|
||||
});
|
||||
|
|
|
@ -1,16 +1,33 @@
|
|||
import axios from "axios"
|
||||
import * as AxiosLogger from 'axios-logger'
|
||||
import cors from "cors"
|
||||
import express, { Request, Response } from "express"
|
||||
import fileUpload, { UploadedFile } from "express-fileupload"
|
||||
import proxy from "express-http-proxy"
|
||||
import requestId from "express-request-id"
|
||||
import morgan from 'morgan'
|
||||
import path from "path"
|
||||
import R from "ramda"
|
||||
import shortid from "shortid"
|
||||
import { Logger } from "winston"
|
||||
import logger from "./logger"
|
||||
|
||||
// import * as AxiosLogger from 'axios-logger'
|
||||
// AxiosLogger.setGlobalConfig({
|
||||
// prefixText: 'your prefix',
|
||||
// dateFormat: 'HH:MM:ss',
|
||||
// status: true,
|
||||
// headers: false,
|
||||
// data: false
|
||||
// });
|
||||
// // add interceptors
|
||||
// siad.interceptors.request.use(
|
||||
// AxiosLogger.requestLogger,
|
||||
// AxiosLogger.errorLogger
|
||||
// );
|
||||
// siad.interceptors.response.use(
|
||||
// AxiosLogger.responseLogger,
|
||||
// AxiosLogger.errorLogger
|
||||
// );
|
||||
|
||||
const MAX_UPLOAD_FILESIZE = 1000 * 1024 * 1024
|
||||
const SIAD_ENDPOINT = "http://localhost:9980"
|
||||
|
@ -28,9 +45,6 @@ const siad = axios.create({
|
|||
}
|
||||
})
|
||||
|
||||
// add interceptors
|
||||
siad.interceptors.request.use(AxiosLogger.requestLogger, AxiosLogger.errorLogger);
|
||||
siad.interceptors.response.use(AxiosLogger.responseLogger, AxiosLogger.errorLogger);
|
||||
|
||||
// Ramda shared utility functions
|
||||
const selectFile = R.path(["files", "file"])
|
||||
|
@ -65,17 +79,19 @@ export class Server {
|
|||
}
|
||||
|
||||
private configureMiddleware() {
|
||||
// add request id middleware (add unique id to X-Request-Id header)
|
||||
this.app.use(requestId())
|
||||
|
||||
// add morgan middleware (HTTP request logging)
|
||||
const options = {
|
||||
stream: {
|
||||
write: (msg: string) => { this.logger.info(msg) }
|
||||
write: (msg: string) => {
|
||||
this.logger.info(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.app.use(morgan("combined", options));
|
||||
|
||||
// add request id middleware (add unique id to X-Request-Id header)
|
||||
this.app.use(requestId())
|
||||
|
||||
// configure CORS (simply enable all CORS requests)
|
||||
this.app.use(cors())
|
||||
|
||||
|
@ -100,6 +116,87 @@ export class Server {
|
|||
}
|
||||
})
|
||||
)
|
||||
|
||||
this.app.get(
|
||||
"/web/:hash", (req: Request, res: Response) => {
|
||||
const { hash } = req.params
|
||||
this.logger.info(`GET /web/:hash -> ${hash}`)
|
||||
res.sendFile(path.join(__dirname + '/testing.html'));
|
||||
}
|
||||
)
|
||||
|
||||
this.app.get(
|
||||
"/:hash", (req: Request, res: Response) => {
|
||||
const { hash } = req.params
|
||||
this.logger.info(`GET /:hash -> ${hash}`)
|
||||
res.sendFile(path.join(__dirname + '/testing.html'));
|
||||
}
|
||||
)
|
||||
|
||||
this.app.get(
|
||||
"/stats", this.handleStatsGET.bind(this)
|
||||
// TODO: redirect to protalstats
|
||||
// proxy("http://localhost:9980/renter/portalstats", {
|
||||
// proxyReqOptDecorator: (opts, _) => {
|
||||
// opts.headers["User-Agent"] = "Sia-Agent"
|
||||
// return opts
|
||||
// },
|
||||
// })
|
||||
)
|
||||
}
|
||||
|
||||
private async handleStatsGET(req: Request, res: Response): Promise<Response> {
|
||||
// + pSeries = p80-1, p95-1, p99-1, p80-24, p95-24, p99-24, p80-168, p95-168, p99-168
|
||||
// + p80-1 means the p80 on all requests over the past 1 hour
|
||||
// + pSeries on ttfb for downloads over 4 MiB in size
|
||||
// + pSeries on total time to download a file under 256 KiB in size
|
||||
// + p80, p95, p99 on total time to download a file under 1 MiB in size
|
||||
// + p80, p95, p99 on download throughput
|
||||
// + p80. p95, p99 on upload throughput
|
||||
// + Total number of files pinned
|
||||
// + Total number of sialink requests served in the past 1, 24, 168 hours
|
||||
// + Total amount of data uploaded in past 1, 24, 168 hours
|
||||
// + total amount of data downloaded in past 1, 24, 168 hours
|
||||
// + total amount of money spent in the past 1, 24, 168, 720, 2160 hours (7,
|
||||
// 30, 90 days for the last values)
|
||||
const mockPSeries = {
|
||||
'p80-1': 40,
|
||||
'p95-1': 44,
|
||||
'p99-1': 48,
|
||||
'p80-24': 42,
|
||||
'p95-24': 44,
|
||||
'p99-24': 46,
|
||||
'p80-168': 41,
|
||||
'p95-168': 45,
|
||||
'p99-168': 49,
|
||||
}
|
||||
const mockSeries = {
|
||||
1: 22,
|
||||
24: 438,
|
||||
168: 2389,
|
||||
}
|
||||
const mockSeriesLarge = {
|
||||
1: 22,
|
||||
24: 438,
|
||||
168: 2389,
|
||||
720: 12045,
|
||||
2160: 63900
|
||||
}
|
||||
|
||||
const data = {
|
||||
'ttfb': mockPSeries,
|
||||
'download_small': mockPSeries,
|
||||
'download_throughput': mockPSeries,
|
||||
'download_total': mockSeries,
|
||||
'upload_small': mockPSeries,
|
||||
'upload_throughput': mockPSeries,
|
||||
'upload_total': mockSeries,
|
||||
'money_spent': mockSeriesLarge,
|
||||
'total_files_pinned': 143,
|
||||
'total_files_served': mockSeries,
|
||||
}
|
||||
|
||||
return res.send(data)
|
||||
}
|
||||
|
||||
private async verifyConnection(): Promise<string | null> {
|
||||
|
@ -165,6 +262,7 @@ export class Server {
|
|||
return res.status(500).send({ error: err.message })
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = new Server(logger).app
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<body>
|
||||
<video width="320" height="240" controls autoplay loop muted>
|
||||
<source src="http://siasky.net/direct/AAAtQI8_78U_ytrCBuhgBdF4lcO6-ehGt8m4f9MsrqlrHA" type="video/mp4">
|
||||
</video>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue