This commit is contained in:
PJ 2020-02-04 19:14:41 +01:00
commit ebd0b5ee48
35 changed files with 29021 additions and 2 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.swo
*.swp

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Nebulous
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,3 +1,10 @@
# SiaWebViewer
# Skynet Portal
Secret project.
## Setup Guide
A setup guide with scripts to install prerequisites can be found in the
[setup-scripts](./setup-scripts) directory.
Once the setup guide is complete you will be running:
- `siad` configured as a ViewNode
- a NodeJS app running a Skynet Portal
- an nginx proxy

6496
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

22
package.json Normal file
View File

@ -0,0 +1,22 @@
{
"private": true,
"scripts": {
"client-dev": "yarn workspace siaviewnode-client dev -p 3000",
"server-dev": "yarn workspace siaviewnode-server dev",
"client-prod": "yarn workspace siaviewnode-client build && yarn workspace siaviewnode-client start -p 3000",
"server-prod": "yarn workspace siaviewnode-server build && yarn workspace siaviewnode-server start",
"dev": "concurrently \"yarn client-dev\" \"yarn server-dev\"",
"start": "NODE_ENV=production concurrently \"yarn client-prod\" \"yarn server-prod\""
},
"workspaces": {
"packages": [
"packages/*"
]
},
"devDependencies": {
"lerna": "3.16.5"
},
"dependencies": {
"concurrently": "5.0.0"
}
}

View File

@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />

View File

@ -0,0 +1,32 @@
{
"name": "siaviewnode-client",
"version": "0.0.1",
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@emotion/core": "10.0.21",
"@material-ui/core": "4.5.1",
"@mdx-js/react": "1.5.1",
"axios": "0.19.0",
"file-saver": "2.0.2",
"isomorphic-unfetch": "3.0.0",
"next": "9.1.1",
"ramda": "0.26.1",
"react": "16.10.2",
"react-dom": "16.10.2",
"react-dropzone": "10.2.1",
"streamsaver": "2.0.3",
"theme-ui": "0.2.44",
"typescript": "3.6.4"
},
"devDependencies": {
"@types/node": "12.11.1",
"@types/ramda": "0.26.36",
"@types/theme-ui": "0.2.3"
}
}

View File

@ -0,0 +1,33 @@
import React from 'react'
import App from 'next/app'
import CssBaseline from '@material-ui/core/CssBaseline'
import { ThemeProvider } from 'theme-ui'
import { theme } from '../theme/theme'
class MyApp extends App {
// Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.
//
// static async getInitialProps(appContext) {
// // calls page's `getInitialProps` and fills `appProps.pageProps`
// const appProps = await App.getInitialProps(appContext);
//
// return { ...appProps }
// }
render() {
const { Component, pageProps } = this.props
return (
<React.Fragment>
<ThemeProvider theme={theme}>
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</React.Fragment>
)
}
}
export default MyApp

View File

@ -0,0 +1,24 @@
import Document, { Html, Head, Main, NextScript } from "next/document"
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head>
<script src="https://unpkg.com/web-streams-polyfill/dist/polyfill.min.js"></script>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument

View File

@ -0,0 +1,106 @@
/** @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";
const Index = () => {
const [value, setValue] = useState(1)
const [linkfileUrl, setInput] = useState("")
const handleChange = (event, newValue) => {
setValue(newValue)
}
const directView = () => {
const removeHead = R.compose(R.tail, R.split("sia://"))
const hash = removeHead(linkfileUrl)[0]
if (window) {
var win = window.open(`/${hash}`, "_blank")
win.focus()
}
}
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>
<AppBar position="static">
<Tabs
value={value}
onChange={handleChange}
aria-label="simple tabs example"
>
<Tab label="Upload" />
<Tab label="Download" />
</Tabs>
</AppBar>
</Container>
</Box>
<Container>
<TabPanel value={value} index={0}>
<Card sx={{ width: "100%" }}>
<CardContent>
<Dropzone />
</CardContent>
</Card>
</TabPanel>
</Container>
<Container>
<TabPanel value={value} index={1}>
<Card sx={{ width: "100%" }}>
<CardContent>
<Flex
sx={{
height: 400,
justifyContent: "center",
alignItems: "center",
flexDirection: "column"
}}
>
<p>Download a file by pasting in a Sia linkfile below:</p>
<Box sx={{ width: "60%" }}>
<Input
placeholder="sia://"
value={linkfileUrl}
onChange={e => setInput(e.target.value)}
sx={{ width: "100%" }}
/>
</Box>
<Box sx={{ mt: 3 }}>
<Button
variant="contained"
color="primary"
onClick={directView}
>
Download
</Button>
</Box>
</Flex>
</CardContent>
</Card>
</TabPanel>
</Container>
</>
)
}
export default Index

View File

@ -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

View File

@ -0,0 +1,82 @@
/** @jsx jsx */
import { CircularProgress } from "@material-ui/core"
import * as R from "ramda"
import { useCallback, useState } from "react"
import { useDropzone } from "react-dropzone"
import { Box, Flex, jsx } from "theme-ui"
/**
* nginx is setup to automatically handle and rewrite the url path.
*/
const API_ENDPOINT =
process.env.NODE_ENV === "development" ? "http://localhost:4000" : "/api"
const pName = R.prop("name")
const splitFilename = R.compose(R.head, R.split(".sia"))
function MyDropzone() {
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const [link, setLink] = useState("")
const onDrop = useCallback(
acceptedFiles => {
setLoading(true)
const file = R.head(acceptedFiles)
const url = API_ENDPOINT + "/skyfile"
const fd = new FormData()
fd.append("file", file)
fetch(url, {
method: "POST",
body: fd,
mode: "cors"
})
.then(res => {
return res.json()
})
.then(({ skylink }) => {
console.log("WE OUT HERE BOYS", skylink)
setLink(`sia://${skylink}`)
setLoading(false)
})
.catch(e => {
console.log("Upload error:", e)
setError("An unexpected error occured. Check console for details.")
setLoading(false)
})
},
[loading, setLoading, error, setError]
)
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop })
return (
<Box>
{link ? (
<Flex
sx={{ height: 400, justifyContent: "center", alignItems: "center" }}
>
<h5>{link}</h5>
</Flex>
) : (
<Flex
{...getRootProps()}
sx={{ height: 400, justifyContent: "center", alignItems: "center" }}
>
<input {...getInputProps()} />
{isDragActive && !loading && !error && !link && (
<p>Drop file here ...</p>
)}
{!isDragActive && !loading && !error && !link && (
<p>Drag 'n' drop a file here, or click to select a file</p>
)}
{loading && <CircularProgress />}
{error && !loading && <h5>{error}</h5>}
</Flex>
)}
</Box>
)
}
export default MyDropzone

View File

@ -0,0 +1,6 @@
/** @jsx jsx */
import { Box, jsx } from "theme-ui"
export const TabPanel = ({ children, value, index, ...props }) => {
return <Box>{value === index && <Box sx={{ p: 3 }}>{children}</Box>}</Box>
}

View File

@ -0,0 +1,13 @@
// example theme.js
export const theme = {
fonts: {
body: 'system-ui, sans-serif',
heading: '"Avenir Next", sans-serif',
monospace: 'Menlo, monospace'
},
colors: {
text: '#000',
background: '#fff',
primary: '#33e'
}
}

View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"exclude": [
"node_modules"
],
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
]
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
node_modules
build
npm-debug.log
error.log
info.log

View File

@ -0,0 +1,22 @@
# Custom Webpack Configuration With Typescript Example
## How to use
Download the example (or clone the whole project)[https://github.com/palmerhq/backpack.git]:
```bash
curl https://codeload.github.com/palmerhq/backpack/tar.gz/master | tar -xz --strip=2 backpack-master/examples/with-typescript
cd with-typescript
```
Install it and run:
```bash
npm install
npm run dev
```
## Idea behind the example
This demonstrates how to customize the underlying Webpack 2 configuration in a Backpack project using a `backpack.config.js` file.
The app itself is exactly the same project as the Webpack configuration example. However, the express application is written in TypeScript.

View File

@ -0,0 +1,20 @@
module.exports = {
webpack: (config, options, webpack) => {
config.entry.main = [
'./src/main.ts'
]
config.resolve = {
extensions: [".ts", ".js", ".json"]
};
config.module.rules.push(
{
test: /\.ts$/,
loader: 'awesome-typescript-loader'
}
);
return config
}
}

View File

@ -0,0 +1,554 @@
{
"name": "siaviewnode-server",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/body-parser": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz",
"integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==",
"requires": {
"@types/connect": "*",
"@types/node": "*"
}
},
"@types/connect": {
"version": "3.4.33",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
"integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
"requires": {
"@types/node": "*"
}
},
"@types/cors": {
"version": "2.8.6",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.6.tgz",
"integrity": "sha512-invOmosX0DqbpA+cE2yoHGUlF/blyf7nB0OGYBBiH27crcVm5NmFaZkLP4Ta1hGaesckCi5lVLlydNJCxkTOSg==",
"requires": {
"@types/express": "*"
},
"dependencies": {
"@types/express": {
"version": "4.17.2",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.2.tgz",
"integrity": "sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==",
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/serve-static": "*"
}
}
}
},
"@types/dateformat": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-3.0.1.tgz",
"integrity": "sha512-KlPPdikagvL6ELjWsljbyDIPzNCeliYkqRpI+zea99vBBbCIA5JNshZAwQKTON139c87y9qvTFVgkFd14rtS4g=="
},
"@types/express-fileupload": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@types/express-fileupload/-/express-fileupload-1.1.0.tgz",
"integrity": "sha512-l7ElHOlTt3Dis1My3LvEwP4tDw/cYiM1sor5nQY8aRm8HPVwalG48lf7ym9k2/z/PwRGADABGgIhv0a6y7XzoA==",
"requires": {
"@types/express": "*"
},
"dependencies": {
"@types/express": {
"version": "4.17.2",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.2.tgz",
"integrity": "sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==",
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/serve-static": "*"
}
}
}
},
"@types/express-http-proxy": {
"version": "1.5.12",
"resolved": "https://registry.npmjs.org/@types/express-http-proxy/-/express-http-proxy-1.5.12.tgz",
"integrity": "sha512-QnTNKXq3xqVU21iMtz1P1M+rhusjGqNqpcbmGzvnkrLNNCiFy6UNK/JDIYJjs3bu8AopFpnBn3wJC16K4nKSgQ==",
"requires": {
"@types/express": "*"
},
"dependencies": {
"@types/express": {
"version": "4.17.2",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.2.tgz",
"integrity": "sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==",
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/serve-static": "*"
}
}
}
},
"@types/express-request-id": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@types/express-request-id/-/express-request-id-1.4.1.tgz",
"integrity": "sha512-39g9HGiBJBjsSJZ80vYgkR4yvu7XVL2R/R/KezU/060gnW5e9btRP7d0R8QORzJ3/Uo7Sis36AM58KSQQOfgcw==",
"requires": {
"@types/express-serve-static-core": "*"
}
},
"@types/express-serve-static-core": {
"version": "4.17.2",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz",
"integrity": "sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg==",
"requires": {
"@types/node": "*",
"@types/range-parser": "*"
}
},
"@types/mime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
"integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw=="
},
"@types/morgan": {
"version": "1.7.37",
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.7.37.tgz",
"integrity": "sha512-tIdEA10BcHcOumMmUiiYdw8lhiVVq62r0ghih5Xpp4WETkfsMiTUZL4w9jCI502BBOrKhFrAOGml9IeELvVaBA==",
"requires": {
"@types/express": "*"
},
"dependencies": {
"@types/express": {
"version": "4.17.2",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.2.tgz",
"integrity": "sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==",
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/serve-static": "*"
}
}
}
},
"@types/node": {
"version": "13.1.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.1.8.tgz",
"integrity": "sha512-6XzyyNM9EKQW4HKuzbo/CkOIjn/evtCmsU+MUM1xDfJ+3/rNjBttM1NgN7AOQvN6tP1Sl1D1PIKMreTArnxM9A=="
},
"@types/range-parser": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
},
"@types/serve-static": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
"integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==",
"requires": {
"@types/express-serve-static-core": "*",
"@types/mime": "*"
}
},
"@types/shortid": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/shortid/-/shortid-0.0.29.tgz",
"integrity": "sha1-gJPuBBam4r8qpjOBCRFLP7/6Dps="
},
"@types/winston": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/@types/winston/-/winston-2.4.4.tgz",
"integrity": "sha512-BVGCztsypW8EYwJ+Hq+QNYiT/MUyCif0ouBH+flrY66O5W+KIXAMML6E/0fJpm7VjIzgangahl5S03bJJQGrZw==",
"requires": {
"winston": "*"
}
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"async": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"requires": {
"lodash": "^4.17.14"
}
},
"axios-logger": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/axios-logger/-/axios-logger-2.3.0.tgz",
"integrity": "sha512-UOTeDhBkqfqgxHq0Q4zQgNSmGIWk3ilPB+ZuuXRgOFuxCrjVmZylZMao6KPsUPrs1Srt0gpzwL7aSAPKr+Ml4A==",
"requires": {
"@types/dateformat": "^3.0.1",
"chalk": "^2.4.1",
"dateformat": "^3.0.3"
}
},
"basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
"requires": {
"safe-buffer": "5.1.2"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"color": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz",
"integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==",
"requires": {
"color-convert": "^1.9.1",
"color-string": "^1.5.2"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"color-string": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
"integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
"requires": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
},
"colornames": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz",
"integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y="
},
"colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
},
"colorspace": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz",
"integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==",
"requires": {
"color": "3.0.x",
"text-hex": "1.0.x"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"dateformat": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
"integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q=="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
},
"dependencies": {
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"diagnostics": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz",
"integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==",
"requires": {
"colorspace": "1.1.x",
"enabled": "1.0.x",
"kuler": "1.0.x"
}
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"enabled": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz",
"integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=",
"requires": {
"env-variable": "0.0.x"
}
},
"env-variable": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz",
"integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"express-request-id": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/express-request-id/-/express-request-id-1.4.1.tgz",
"integrity": "sha512-qpxK6XhDYtdx9FvxwCHkUeZVWtkGbWR87hBAzGECfwYF/QQCPXEwwB2/9NGkOR1tT7/aLs9mma3CT0vjSzuZVw==",
"requires": {
"uuid": "^3.3.2"
}
},
"fast-safe-stringify": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
"integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
},
"fecha": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
"integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg=="
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"kuler": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz",
"integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==",
"requires": {
"colornames": "^1.1.1"
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
"logform": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz",
"integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==",
"requires": {
"colors": "^1.2.1",
"fast-safe-stringify": "^2.0.4",
"fecha": "^2.3.3",
"ms": "^2.1.1",
"triple-beam": "^1.3.0"
}
},
"morgan": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
"integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
"requires": {
"basic-auth": "~2.0.0",
"debug": "2.6.9",
"depd": "~1.1.2",
"on-finished": "~2.3.0",
"on-headers": "~1.0.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
},
"one-time": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz",
"integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4="
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"readable-stream": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz",
"integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
},
"simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
"requires": {
"is-arrayish": "^0.3.1"
}
},
"stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"requires": {
"has-flag": "^3.0.0"
}
},
"text-hex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
},
"triple-beam": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
"integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
},
"winston": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz",
"integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==",
"requires": {
"async": "^2.6.1",
"diagnostics": "^1.1.1",
"is-stream": "^1.1.0",
"logform": "^2.1.1",
"one-time": "0.0.4",
"readable-stream": "^3.1.1",
"stack-trace": "0.0.x",
"triple-beam": "^1.3.0",
"winston-transport": "^4.3.0"
}
},
"winston-transport": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz",
"integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==",
"requires": {
"readable-stream": "^2.3.6",
"triple-beam": "^1.2.0"
},
"dependencies": {
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
}
}
}

View File

@ -0,0 +1,38 @@
{
"name": "siaviewnode-server",
"version": "0.0.1",
"scripts": {
"start": "PORT=4000 node ./build/main.js",
"dev": "PORT=4000 backpack dev",
"build": "backpack build"
},
"license": "MIT",
"dependencies": {
"@types/cors": "^2.8.6",
"@types/express": "^4.0.34",
"@types/express-fileupload": "^1.1.0",
"@types/express-http-proxy": "^1.5.12",
"@types/express-request-id": "^1.4.1",
"@types/morgan": "^1.7.37",
"@types/shortid": "0.0.29",
"@types/winston": "^2.4.4",
"awesome-typescript-loader": "^5.2.1",
"axios": "0.19.0",
"axios-logger": "^2.3.0",
"backpack-core": "^0.8.4",
"cors": "2.8.5",
"express": "^4.14.0",
"express-fileupload": "1.1.6",
"express-http-proxy": "1.6.0",
"express-request-id": "^1.4.1",
"express-session": "1.17.0",
"morgan": "^1.9.1",
"ramda": "0.26.1",
"shortid": "2.2.15",
"typescript": "^3.5.2",
"winston": "^3.2.1"
},
"devDependencies": {
"@types/ramda": "0.26.36"
}
}

View File

@ -0,0 +1,28 @@
import { createLogger, format, transports } from 'winston';
const { combine, timestamp, prettyPrint } = format;
const logger = createLogger({
format: combine(
timestamp(),
prettyPrint(),
),
transports: [
new transports.Console(),
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,
});
export default logger;

View File

@ -0,0 +1,240 @@
import axios from "axios"
import cors from "cors"
import express, { Request, Response } from "express"
import fs from "fs";
import fileUpload, { UploadedFile } from "express-fileupload"
import { homedir } from "os"
import proxy from "express-http-proxy"
import requestId from "express-request-id"
import morgan from 'morgan'
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"
// simple siad connection with static strings
const siad = axios.create({
baseURL: SIAD_ENDPOINT,
headers: {
"User-Agent": "Sia-Agent",
"Access-Control-Allow-Origin": "*"
},
auth: {
username: "",
password: fs.readFileSync(homedir().concat("/.sia/apipassword"), "utf8").trim()
}
})
// Ramda shared utility functions
const selectFile = R.path(["files", "file"])
const pName = R.prop("name")
export class Server {
public app: express.Express
constructor(private logger: Logger) { this.boot() }
private boot() {
this.app = express()
this.logger.info("Configuring middleware...")
this.configureMiddleware()
this.logger.info("Configuring routes...")
this.configureRoutes()
this.logger.info("Booting server...")
const port = parseInt(process.env.PORT, 10) || 3000
this.app.listen(port, () => {
this.logger.info(`Listening on port ${port}`)
})
this.logger.info("Verifying connection to siad...")
this.verifyConnection().then((version: string | null) => {
if (version) {
this.logger.info(`siad reachable, version ${version}`)
}
})
}
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)
}
}
}
this.app.use(morgan("combined", options));
// configure CORS (simply enable all CORS requests)
this.app.use(cors())
// configure upload limits
this.app.use(fileUpload({ limits: { fileSize: MAX_UPLOAD_FILESIZE } }))
}
private configureRoutes() {
this.app.post("/siafile", this.handleSiafilePOST.bind(this))
this.app.post("/skyfile", this.handleSkyfilePOST.bind(this))
this.app.get(
"/skylink/:hash",
proxy("http://127.0.0.1:9980/skynet/skylink/", {
proxyReqOptDecorator: (opts, _) => {
opts.headers["User-Agent"] = "Sia-Agent"
return opts
},
proxyReqPathResolver: req => {
const { hash } = req.params
return `/skynet/skylink/${hash}`
}
})
)
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> {
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> {
try {
const resp = await siad.get('/daemon/version')
return resp.data.version
} catch (err) {
const { message } = err;
this.logger.error(message)
return null
}
}
private async handleSkyfilePOST(req: Request, res: Response): Promise<Response> {
const file = selectFile(req) as UploadedFile
const uid = shortid.generate()
this.logger.info(`POST skyfile w/name ${file.name} and uid ${uid}`)
try {
const { data } = await siad.post(
`/skynet/skyfile/${uid}`,
file.data,
{
maxContentLength: MAX_UPLOAD_FILESIZE,
params: { name: file.name }
}
)
return res.send(data)
} catch (err) {
const { message } = err;
this.logger.error(message)
return res.status(500).send({ error: err.message })
}
}
private async handleSiafilePOST(req: Request, res: Response): Promise<Response> {
const file = selectFile(req) as UploadedFile
const selectContentLength = R.path(["headers", "Content-Length"])
const cl = selectContentLength(req)
this.logger.info(`POST siafile ${file.name} w/contentlength ${cl}`)
try {
const { data: stream, headers } = await siad.post(
"/renter/stream",
file.data,
{ responseType: "stream" }
)
const splitFilename = R.compose(R.head, R.split(".sia"))
const fileName = R.compose(splitFilename, pName)(file)
res.set(
"Content-Disposition",
`attachment; filename="${fileName}"; filename*="${fileName}"`
)
res.set("Content-Length", headers["Content-Length"])
return stream.pipe(res)
} catch (err) {
const { message } = err;
this.logger.error(message)
return res.status(500).send({ error: err.message })
}
}
}
module.exports = new Server(logger).app

View File

@ -0,0 +1,25 @@
{
"compilerOptions": {
"module": "commonjs",
"noImplicitReturns": true,
"noUnusedLocals": true,
"sourceMap": true,
"strict": false,
"lib": [
"es2017",
"dom"
],
"target": "es2017",
"esModuleInterop": true
},
"compileOnSave": true,
"exclude": [
"node_modules",
"../../node_modules"
],
"awesomeTypescriptLoaderOptions": {
"reportFiles": [
"./src/**/*"
]
}
}

File diff suppressed because it is too large Load Diff

10
pm2.json Normal file
View File

@ -0,0 +1,10 @@
{
"apps": [
{
"name": "siaviewer",
"script": "yarn",
"args": "start",
"interpreter": "/bin/bash"
}
]
}

75
setup-scripts/README.md Normal file
View File

@ -0,0 +1,75 @@
# Skynet Portal Setup Scripts
This directory contains a setup guide and scripts that will install and
configure some basic requirements for running a Skynet Portal. The assumption is
that we are working with a Debian Buster Minimal system or similar.
## Initial Setup
(Assumes we are logged in as root on a fresh installation of Debian)
You may want to fork this repository and add your ssh pubkey to
`authorized_keys` and optionally edit the `tmux` and `bash` configurations.
0. SSH in a freshly installed Debian machine.
1. `apt-get update && apt-get install sudo`
2. `adduser user`
3. `usermod -a -G sudo user`
4. Quit the ssh session.
You a can now ssh into your machine as the user `user`.
5. On your local machine: `ssh-copy-id user@ip-addr`
6. On your local machine: `ssh user@ip-addr`
7. Now logged in as `user`: `sudo apt-get install git`
8. `git clone https://github.com/NebulousLabs/skynet-webportal`
9. `cd skynet-webportal/setup-scripts`
11. `./setup.sh`
12. Once DNS records are set you can run: `./letsencrypt-setup.sh`
13. You should also change the nginx configuration to listen on port 443
instead.
## Setting up siad
NOTE: You must be running `siad` and `siac` by building from the `viewnode`
branch.
You still need to setup `siad` for the backend to be complete.
1. `cd ~/; mkdir siad`
2. `nohup siad &>/dev/null &`
This will start syncing `siad` in the background.
## ViewNode setup
When `siad` is done syncing, create a new wallet and unlock the wallet.
Then set an allowance (`siac renter setallowance`), with the suggested values
below:
- 10 KS (keep 25 KS in your wallet)
- default period
- default number of hosts
- 8 week renewal time
- 500 GB expected storage
- 500 GB expected upload
- 5 TB expected download
- default redundancy
Once your allowance is set you need to set your node to be a viewnode with the
following command:
`siac renter setallowance --payment-contract-initial-price 10SC`
Now your node will begin making 10 contracts per block with many hosts so it can
potentially view the whole network's files.
## Running the Portal
`cd` into the parent directory and run `yarn` to build dependencies.
We recommend running the Portal through `pm2` (a nodejs process manager) in the background with the command:
`pm2 --name skynet start npm -- start`
`yarn start` will also work if not using `pm2`.
The Protal which will automatically read your `siad` API password and startup a
portal on `localhost:3000`. nginx will expose this to port 80 or 443 if you
configured it for SSL.

View File

114
setup-scripts/bashrc Normal file
View File

@ -0,0 +1,114 @@
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth
# append to the history file, don't overwrite it
shopt -s histappend
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar
# make less more friendly for non-text input files, see lesspipe(1)
#[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
debian_chroot=$(cat /etc/debian_chroot)
fi
# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
alacritty|xterm-color|*-256color) color_prompt=yes;;
esac
# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes
if [ -n "$force_color_prompt" ]; then
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
# We have color support; assume it's compliant with Ecma-48
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
# a case would tend to support setf rather than setaf.)
color_prompt=yes
else
color_prompt=
fi
fi
if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt
# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
;;
*)
;;
esac
# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
alias ls='ls --color=auto'
#alias dir='dir --color=auto'
#alias vdir='vdir --color=auto'
#alias grep='grep --color=auto'
#alias fgrep='fgrep --color=auto'
#alias egrep='egrep --color=auto'
fi
# colored GCC warnings and errors
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
# some more ls aliases
#alias ll='ls -l'
#alias la='ls -A'
#alias l='ls -CF'
# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; then
if [ -f /usr/share/bash-completion/bash_completion ]; then
. /usr/share/bash-completion/bash_completion
elif [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi
fi
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/usr/local/go/bin:/home/user/go/bin

View File

@ -0,0 +1,11 @@
#! /usr/bin/env bash
set -e
domain="$1"
if [[ -z $domain ]]; then
echo "Usage $0 DOMAIN_NAME"
exit 1
fi
sudo certbot --nginx -d "$domain" -d www."$domain"
sudo certbot renew --dry-run

68
setup-scripts/setup.sh Executable file
View File

@ -0,0 +1,68 @@
#! /usr/bin/env bash
set -e
# Copy over basic configuration files.
cp ./tmux.conf ~/.tmux.conf
cp ./bashrc ~/.bashrc
source ~/.bashrc
# Add SSH keys and set SSH configs
sudo cp ./ssh_config /etc/ssh/ssh_config
mkdir -p ~/.ssh
cat ./authorized_keys >> ~/.ssh/authorized_keys
# Nodejs install prerequisite. From official documentation.
curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash -
# Yarn install prerequisite.
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
# Apt installations.
sudo apt-get update
sudo apt-get -y install ufw tmux ranger htop nload nginx certbot \
python-certbot-nginx nodejs gcc g++ make yarn git vim
# Install pm2
sudo npm i -g pm2
# terminfo for alacritty terminal via ssh
# If you don't use the alacritty terminal you can remove this step.
wget -c https://raw.githubusercontent.com/alacritty/alacritty/master/extra/alacritty.info
sudo tic -xe alacritty,alacritty-direct alacritty.info
rm alacritty.info
# Setup nginx config
sudo cp ./skynet-nginx.conf /etc/nginx/sites-available/skynet
sudo nginx -t
sudo ln -s /etc/nginx/sites-available/skynet /etc/nginx/sites-enabled/skynet
sudo rm /etc/nginx/sites-enabled/default
sudo systemctl reload nginx
# Setup firewall
# TODO: disable plain HTTP eventually
sudo ufw enable
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw allow 'Nginx HTTP'
# Install Go 1.13.7.
wget -c https://dl.google.com/go/go1.13.7.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.13.7.linux-amd64.tar.gz
source ~/.bashrc
rm go1.13.7.linux-amd64.tar.gz
# Sanity check that will pass if go was installed correctly.
go version
cwd=$(pwd)
# Install Sia
cd ~/
git clone https://gitlab.com/NebulousLabs/Sia
cd Sia && git checkout viewnode && make
# Setup skynet frontend.
cd $cwd
cd ../
yarn

View File

@ -0,0 +1,69 @@
server {
listen 80;
location / {
client_max_body_size 1000M;
#post_max_size 1000M;
proxy_read_timeout 600;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location ^~ /api/ {
client_max_body_size 1000M;
#post_max_size 1000M;
proxy_read_timeout 600;
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://127.0.0.1:4000/;
}
location ~ "^/[a-zA-Z0-9-_+/]{46}$" {
client_max_body_size 1000M;
#post_max_size 1000M;
proxy_read_timeout 600;
proxy_set_header User-Agent: "Sia-Agent";
proxy_set_header Host: localhost;
proxy_pass http://127.0.0.1:4000/skylink$request_uri;
}
location ~ "^/web/([a-zA-Z0-9-_+/]{46})$" {
client_max_body_size 1000M;
#post_max_size 1000M;
proxy_read_timeout 600;
proxy_set_header User-Agent: "Sia-Agent";
proxy_set_header Host: localhost;
proxy_pass http://127.0.0.1:4000/skylink/$1;
sub_filter 'sia://' 'https://$host/';
sub_filter_once off;
sub_filter_types text/plain;
}
location /direct/ {
client_max_body_size 1000M;
#post_max_size 1000M;
proxy_read_timeout 600;
proxy_set_header User-Agent: "Sia-Agent";
proxy_pass http://127.0.0.1:9980/skynet/skylink/;
}
location /uploaddirect/ {
client_max_body_size 1000M;
#post_max_size 1000M;
proxy_read_timeout 600;
proxy_set_header User-Agent: "Sia-Agent";
proxy_pass http://127.0.0.1:9980/renter/linkfile/linkfiles/externaluploads/;
}
location /stream/ {
client_max_body_size 1000M;
#post_max_size 1000M;
proxy_read_timeout 600;
proxy_set_header User-Agent: "Sia-Agent";
proxy_pass http://127.0.0.1:9980/renter/stream/;
}
}

51
setup-scripts/ssh_config Normal file
View File

@ -0,0 +1,51 @@
# This is the ssh client system-wide configuration file. See
# ssh_config(5) for more information. This file provides defaults for
# users, and the values can be changed in per-user configuration files
# or on the command line.
# Configuration data is parsed as follows:
# 1. command line options
# 2. user-specific file
# 3. system-wide file
# Any configuration value is only changed the first time it is set.
# Thus, host-specific definitions should be at the beginning of the
# configuration file, and defaults at the end.
# Site-wide defaults for some commonly used options. For a comprehensive
# list of available options, their meanings and defaults, please see the
# ssh_config(5) man page.
Host *
# ForwardAgent no
# ForwardX11 no
# ForwardX11Trusted yes
PasswordAuthentication no
# HostbasedAuthentication no
# GSSAPIAuthentication no
# GSSAPIDelegateCredentials no
# GSSAPIKeyExchange no
# GSSAPITrustDNS no
# BatchMode no
# CheckHostIP yes
# AddressFamily any
# ConnectTimeout 0
# StrictHostKeyChecking ask
# IdentityFile ~/.ssh/id_rsa
# IdentityFile ~/.ssh/id_dsa
# IdentityFile ~/.ssh/id_ecdsa
# IdentityFile ~/.ssh/id_ed25519
# Port 22
# Protocol 2
# Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc
# MACs hmac-md5,hmac-sha1,umac-64@openssh.com
# EscapeChar ~
# Tunnel no
# TunnelDevice any:any
# PermitLocalCommand no
# VisualHostKey no
# ProxyCommand ssh -q -W %h:%p gateway.example.com
# RekeyLimit 1G 1h
SendEnv LANG LC_*
HashKnownHosts no
GSSAPIAuthentication yes

18
setup-scripts/tmux.conf Normal file
View File

@ -0,0 +1,18 @@
# remap prefix from 'C-b' to 'C-a'
unbind C-b
set-option -g prefix C-a
bind-key C-a send-prefix
# split panes using | and -
bind | split-window -h
bind - split-window -v
unbind '"'
unbind %
# reload config file (change file location to your the tmux.conf you want to use)
bind r source-file ~/.tmux.conf
set -g visual-activity off
set -g mouse on
# This copies highlighted text.
set -g mouse-select-window on

10168
yarn.lock Normal file

File diff suppressed because it is too large Load Diff