Add stats route (WIP) + add /web/ testing route (WIP)
This commit is contained in:
parent
34851a7078
commit
b720b5f0d6
|
@ -1,10 +1,10 @@
|
||||||
/** @jsx jsx */
|
/** @jsx jsx */
|
||||||
import { AppBar, Button, Card, CardContent, Container, Input, Tab, Tabs, Typography } from "@material-ui/core"
|
import { AppBar, Button, Card, CardContent, Container, Input, Tab, Tabs, Typography } from "@material-ui/core";
|
||||||
import * as R from "ramda"
|
import * as R from "ramda";
|
||||||
import { useState } from "react"
|
import { useState } from "react";
|
||||||
import { Box, Flex, jsx } from "theme-ui"
|
import { Box, Flex, jsx } from "theme-ui";
|
||||||
import Dropzone from "../src/components/Dropzone"
|
import Dropzone from "../src/components/Dropzone";
|
||||||
import { TabPanel } from "../src/components/TabPanel"
|
import { TabPanel } from "../src/components/TabPanel";
|
||||||
|
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
const [value, setValue] = useState(1)
|
const [value, setValue] = useState(1)
|
||||||
|
@ -31,6 +31,9 @@ const Index = () => {
|
||||||
<Typography sx={{ fontWeight: 700 }}>Sia Skynet</Typography>
|
<Typography sx={{ fontWeight: 700 }}>Sia Skynet</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ ml: "auto" }}>
|
<Box sx={{ ml: "auto" }}>
|
||||||
|
<Button href="/stats">
|
||||||
|
Statistics
|
||||||
|
</Button>
|
||||||
<Button href="https://sia.tech/" target="_blank">
|
<Button href="https://sia.tech/" target="_blank">
|
||||||
About Sia
|
About Sia
|
||||||
</Button>
|
</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: [
|
transports: [
|
||||||
new transports.Console(),
|
new transports.Console(),
|
||||||
new transports.File({ filename: './error.log', level: 'error' }),
|
new transports.File({
|
||||||
new transports.File({ filename: './info.log', level: 'info' }),
|
filename: './error.log',
|
||||||
|
level: 'error',
|
||||||
|
maxsize: 5242880,
|
||||||
|
maxFiles: 2
|
||||||
|
}),
|
||||||
|
new transports.File({
|
||||||
|
filename: './info.log',
|
||||||
|
level: 'info',
|
||||||
|
maxsize: 5242880,
|
||||||
|
maxFiles: 5
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
exitOnError: false,
|
exitOnError: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,16 +1,33 @@
|
||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import * as AxiosLogger from 'axios-logger'
|
|
||||||
import cors from "cors"
|
import cors from "cors"
|
||||||
import express, { Request, Response } from "express"
|
import express, { Request, Response } from "express"
|
||||||
import fileUpload, { UploadedFile } from "express-fileupload"
|
import fileUpload, { UploadedFile } from "express-fileupload"
|
||||||
import proxy from "express-http-proxy"
|
import proxy from "express-http-proxy"
|
||||||
import requestId from "express-request-id"
|
import requestId from "express-request-id"
|
||||||
import morgan from 'morgan'
|
import morgan from 'morgan'
|
||||||
|
import path from "path"
|
||||||
import R from "ramda"
|
import R from "ramda"
|
||||||
import shortid from "shortid"
|
import shortid from "shortid"
|
||||||
import { Logger } from "winston"
|
import { Logger } from "winston"
|
||||||
import logger from "./logger"
|
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 MAX_UPLOAD_FILESIZE = 1000 * 1024 * 1024
|
||||||
const SIAD_ENDPOINT = "http://localhost:9980"
|
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
|
// Ramda shared utility functions
|
||||||
const selectFile = R.path(["files", "file"])
|
const selectFile = R.path(["files", "file"])
|
||||||
|
@ -65,17 +79,19 @@ export class Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
private configureMiddleware() {
|
private configureMiddleware() {
|
||||||
|
// add request id middleware (add unique id to X-Request-Id header)
|
||||||
|
this.app.use(requestId())
|
||||||
|
|
||||||
// add morgan middleware (HTTP request logging)
|
// add morgan middleware (HTTP request logging)
|
||||||
const options = {
|
const options = {
|
||||||
stream: {
|
stream: {
|
||||||
write: (msg: string) => { this.logger.info(msg) }
|
write: (msg: string) => {
|
||||||
|
this.logger.info(msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.app.use(morgan("combined", options));
|
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)
|
// configure CORS (simply enable all CORS requests)
|
||||||
this.app.use(cors())
|
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> {
|
private async verifyConnection(): Promise<string | null> {
|
||||||
|
@ -165,6 +262,7 @@ export class Server {
|
||||||
return res.status(500).send({ error: err.message })
|
return res.status(500).send({ error: err.message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new Server(logger).app
|
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