64 Commits

Author SHA1 Message Date
9a50b2c6cf Dockerize gather 2024-02-04 19:09:33 +03:00
db9180721a Updated drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-07 02:36:58 +03:00
1af1048670 Fix crash
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-06 19:43:44 +03:00
148a3693b9 Merge branch 'monorepo' of ssh://git.radner.ru:3036/anatolykopyl/warframe-center into monorepo
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-05 14:50:16 +03:00
efb1cba216 Remove metrika id from env 2022-07-05 14:50:12 +03:00
755cb06276 Remove metrika id from env
Some checks failed
continuous-integration/drone/push Build was killed
2022-07-05 14:49:12 +03:00
a40390a30e Add ym id to drone secrets
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-05 14:10:50 +03:00
9d5d49c9f2 Fix undefined scan results
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-05 13:31:08 +03:00
fe1cf2f171 Merge branch 'monorepo' of ssh://git.radner.ru:3036/anatolykopyl/warframe-center into monorepo
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build was killed
2022-07-05 12:03:29 +03:00
d3cb737972 Fix ya metrika hopefully 2022-07-05 12:03:08 +03:00
7a0480fa69 Fix ya metrika hopefully
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-05 11:58:01 +03:00
a77fefc251 Switch ya metrika packages
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-05 09:50:16 +03:00
ad10f35e19 Disable ya metrika
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-05 02:34:43 +03:00
0df5022493 Yandex metrica work
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-05 02:19:27 +03:00
ccf1aaba9b Fix empty orders in getSortedOrders
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-04 12:38:56 +03:00
5433c7a1d2 Fixed linting error
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-04 02:40:48 +03:00
0863a73625 Added a help modal
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-04 02:23:09 +03:00
ba28925d3c Modified drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-03 17:41:56 +03:00
b96feef044 Modified drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-03 17:34:52 +03:00
9387994d6d Fixed no db on build error
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-03 17:27:35 +03:00
e13d6a7f4a Translate table headers
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-03 17:14:56 +03:00
b956bb525d Drone build update
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-03 16:59:19 +03:00
3c888dc7f1 Added i18n
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-03 16:31:31 +03:00
f7b78f6d0f Return empty orders on error
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-02 18:58:00 +03:00
04a3ea5a23 Handle api errors
Some checks failed
continuous-integration/drone/push Build is failing
2022-07-02 17:50:25 +03:00
0b808328f4 Path to pm2
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-30 03:30:38 +03:00
55ecb3b12b Edited pipeline
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-30 03:22:45 +03:00
d366262636 Edited pipeline
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-30 03:21:54 +03:00
714ef9c87f Edited pipeline
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-30 03:20:43 +03:00
41116e6ef1 POSIX exec
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-30 03:11:14 +03:00
b56f1b0b62 Full path
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-30 03:06:34 +03:00
5aab4a955b Source interactive shell files
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-30 03:05:20 +03:00
3f60cba322 Edited pipeline
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-30 02:58:00 +03:00
f8e36fc519 Join lines 2022-03-30 02:54:40 +03:00
9937a407de Edited pipeline 2022-03-30 02:49:14 +03:00
92395f9db1 Replace ssh key with pass 2022-03-30 02:47:37 +03:00
f649cc2c14 Edited pipeline 2022-03-30 02:40:26 +03:00
3f3e851590 Source profioe
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-22 17:05:05 +03:00
dc54effc83 ssh client
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-22 17:02:23 +03:00
6951ce5db7 Remove dependency
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-22 17:00:45 +03:00
10516d84e6 Big step 2022-03-22 16:59:42 +03:00
307dfc2433 alpine-ssh 2022-03-22 16:53:53 +03:00
42c66ac8da All docker 2022-03-22 16:53:08 +03:00
7d70bec13b Костыль путь до npm
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-22 16:49:37 +03:00
fcff4f50c1 Twst pwd
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-22 16:42:29 +03:00
72a395e813 Specified port
Some checks failed
continuous-integration/drone/push Build encountered an error
continuous-integration/drone Build is failing
2022-03-22 16:21:58 +03:00
3877a37e7e Removed useless scp
Some checks failed
continuous-integration/drone/push Build encountered an error
2022-03-22 16:14:57 +03:00
cbffc25a78 Updated drone config
Some checks failed
continuous-integration/drone/push Build encountered an error
2022-03-22 16:12:58 +03:00
131a0b23f5 Disclamer
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-19 20:01:58 +03:00
4f54d08666 Got rid of scss files, fixet time format
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-19 15:41:32 +03:00
76bf04f8de Fix redirect and restart frontend on push
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-19 03:38:25 +03:00
12a482324c pm2 file and redirect fix
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-19 03:11:52 +03:00
e733ca9cc3 Renamed pm2 process
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-19 02:59:51 +03:00
66212c9a7d Sequential pipeline
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-19 01:49:17 +03:00
34c1e577f4 Absolute path
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-19 01:45:44 +03:00
3d3d145072 Install deps in pipeline
Some checks failed
continuous-integration/drone/push Build encountered an error
continuous-integration/drone Build is failing
2022-03-19 01:41:44 +03:00
7e31449bd2 added mongo url
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-18 23:23:33 +03:00
9d663510bc Install in workspace
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-18 22:46:35 +03:00
2c4cd8ca5d Docker deploy yml
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-18 22:24:25 +03:00
2198e134d6 Explicit logo size
Some checks failed
continuous-integration/drone/push Build is failing
2022-03-14 23:57:27 +03:00
2df13c117a ci preparations
Some checks failed
continuous-integration/drone Build is failing
2022-03-14 22:46:08 +03:00
789fb6f38d Fixed mongo hanging script 2022-03-14 03:38:29 +03:00
946a9afeac Expanded mongo model 2022-03-14 01:41:52 +03:00
beb5ff052f Material UI 2022-03-14 00:29:29 +03:00
44 changed files with 7214 additions and 9383 deletions

View File

@@ -1,9 +0,0 @@
kind: pipeline
type: ssh
name: default
server:
host: 192.168.1.54
user: pi
password:
from_secret: password

2
.gitignore vendored
View File

@@ -2,3 +2,5 @@ node_modules
.DS_Store
.vscode
.env
.idea
.yarn

View File

@@ -1 +1 @@
MONGODB_URI=
MONGODB_URI=

View File

@@ -1,3 +1,15 @@
{
"extends": ["standard", "standard-jsx", "standard-react", "next/core-web-vitals"]
"extends": ["standard", "standard-jsx", "standard-react", "next/core-web-vitals"],
"rules": {
"import/order": [
"error",
{
"newlines-between": "always",
"alphabetize": {
"order": "asc",
"caseInsensitive": true
}
}
]
}
}

33
app/components/Footer.js Normal file
View File

@@ -0,0 +1,33 @@
import { Box, Typography, Link } from '@mui/material'
import { Component } from 'react'
export default class Footer extends Component {
render () {
return (
<Box
sx={{
p: 12
}}
>
All data is collected from the&nbsp;
<Link
href='https://warframe.market/api_docs'
target='_blank'
rel='noreferrer'
>
Warframe Market API
</Link>
<Typography variant='h6'>DISCLAIMER</Typography>
<Box
sx={{
fontSize: 12,
lineHeight: 'normal'
}}
>
Digital Extremes Ltd, Warframe and the logo Warframe are registered trademarks. All rights are reserved worldwide. This site has no official link with Digital Extremes Ltd or Warframe. All artwork, screenshots, characters or other recognizable features of the intellectual property relating to these trademarks are likewise the intellectual property of Digital Extremes Ltd.
</Box>
</Box>
)
}
}

View File

@@ -0,0 +1,37 @@
import Router from 'next/router'
import React, { useCallback, useEffect } from 'react'
import ym, { YMInitializer } from 'react-yandex-metrika'
const WithYandexMetrika = (props) => {
const { children } = props
const enabled = process.env.NODE_ENV !== 'development'
const hit = useCallback((url) => {
if (enabled) {
ym('hit', url)
} else {
console.log('%c[YandexMetrika](HIT)', 'color: orange', url)
}
}, [enabled])
useEffect(() => {
hit(window.location.pathname + window.location.search)
Router.events.on('routeChangeComplete', hit)
}, [hit])
return (
<>
{enabled && (
<YMInitializer
accounts={[87671663]}
options={{ webvisor: true, defer: true }}
version='2'
/>
)}
{children}
</>
)
}
export default WithYandexMetrika

View File

@@ -1,5 +1,7 @@
import Head from 'next/head'
import Footer from './Footer'
export default function Layout ({ children }) {
return (
<>
@@ -8,6 +10,7 @@ export default function Layout ({ children }) {
<link rel='icon' href='/favicon.ico' />
</Head>
<main>{children}</main>
<Footer />
</>
)
}

View File

@@ -0,0 +1,6 @@
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'ru']
}
}

View File

@@ -1,16 +1,31 @@
const withPlugins = require('next-compose-plugins')
const { i18n } = require('./next-i18next.config')
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
redirects: [
{
source: '/',
destination: '/home',
permanent: true
}
]
i18n
// webpack: (config, { webpack }) => {
// return config
// },
}
module.exports = nextConfig
const redirects = {
async redirects () {
return [
{
source: '/',
destination: '/home',
permanent: true
}
]
}
}
module.exports = withPlugins(
[
[redirects]
],
nextConfig
)

5363
app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,12 +6,22 @@
"start": "next start"
},
"dependencies": {
"@emotion/react": "^11.8.2",
"@emotion/styled": "^11.8.1",
"@mui/icons-material": "^5.5.0",
"@mui/material": "^5.5.0",
"@mui/x-data-grid": "^5.6.1",
"next": "latest",
"next-compose-plugins": "^2.2.1",
"next-i18next": "^11.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-moment": "^1.1.1",
"react-yandex-metrika": "^2.6.0",
"reset-css": "^5.0.1",
"sass": "^1.49.9",
"shared-stuff": "file:../shared-stuff"
"shared-stuff": "file:../shared-stuff",
"sharp": "^0.30.3"
},
"devDependencies": {
"babel-eslint": "^10.1.0",

View File

@@ -1,13 +1,29 @@
import { CssBaseline } from '@mui/material'
import { createTheme, ThemeProvider } from '@mui/material/styles'
import { appWithTranslation } from 'next-i18next'
import 'reset-css'
import '../styles/global.scss'
import Layout from '../components/layout'
import WithYandexMetrika from '../components/WithYandexMetrika'
function App ({ Component, pageProps }) {
const theme = createTheme({
palette: {
mode: 'light'
}
})
function MyApp ({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
<WithYandexMetrika>
<ThemeProvider theme={theme}>
<CssBaseline />
<Layout>
<Component {...pageProps} />
</Layout>
</ThemeProvider>
</WithYandexMetrika>
)
}
export default MyApp
export default appWithTranslation(App)

View File

@@ -0,0 +1,32 @@
import { Dialog, DialogTitle, Box, Typography } from '@mui/material'
import { useTranslation } from 'next-i18next'
export default function HelpModal ({ open, onClose }) {
const { t } = useTranslation('home')
return (
<Dialog
open={open}
onClose={onClose}
>
<DialogTitle>
{t('help_header')}
</DialogTitle>
<Box
sx={{
p: 3
}}
>
<Typography gutterBottom>
{t('help_body_1')}
</Typography>
<Typography gutterBottom>
{t('help_body_2')}
</Typography>
<Typography gutterBottom>
{t('help_body_3')}
</Typography>
</Box>
</Dialog>
)
}

View File

@@ -1,24 +1,73 @@
import { Component } from 'react'
import { Box, Chip } from '@mui/material'
import { useTranslation } from 'next-i18next'
import Image from 'next/image'
import styles from './Hero.module.scss'
import logo from './assets/warframe_logo.png'
import { useState } from 'react'
export default class Hero extends Component {
render () {
return (
<div className={styles.hero}>
<div className={styles.logo}>
import logo from './assets/warframe_logo.png'
import HelpModal from './HelpModal'
const Hero = () => {
const { t } = useTranslation('home')
const [modalOpen, setModalOpen] = useState(false)
const openModal = () => {
setModalOpen(true)
}
const closeModal = () => {
setModalOpen(false)
}
return (
<>
<Box
sx={{
py: 12,
textAlign: 'center'
}}
>
<Chip
label={t('help_header')}
variant='outlined'
onClick={openModal}
sx={{
position: 'absolute',
top: '10px',
right: '10px'
}}
/>
<Box
sx={{
width: 400,
m: 'auto'
}}
>
<Image
src={logo}
alt='logo'
layout='responsive'
width={500}
height={300}
/>
</div>
<div className={styles.text}>
</Box>
<Box
sx={{
width: 400,
m: 'auto'
}}
>
<h1>Market Gaps</h1>
<p>Find a profitable difference between the price of the set and the price of the sum of it's parts.</p>
</div>
</div>
)
}
<p>{t('description')}</p>
</Box>
</Box>
<HelpModal
open={modalOpen}
onClose={closeModal}
/>
</>
)
}
export default Hero

View File

@@ -1,17 +0,0 @@
.hero {
width: 100%;
padding: 128px 0;
position: relative;
background: white;
text-align: center;
overflow: hidden;
.text, .logo {
width: 400px;
margin: auto;
}
.text {
}
}

View File

@@ -1,18 +0,0 @@
import { Component } from 'react'
export default class ItemRow extends Component {
constructor ({ scanResult }) {
super()
this.scanResult = scanResult
}
render () {
return (
<tr>
<td>{this.scanResult.name}</td>
<td>{this.scanResult.partsPrice}</td>
<td>{this.scanResult.setPrice}</td>
</tr>
)
}
}

View File

@@ -1,25 +1,98 @@
import React from 'react'
import ItemRow from './ItemRow'
import { Link, Typography, Box } from '@mui/material'
import { DataGrid } from '@mui/x-data-grid'
import { Component } from 'react'
import { withTranslation } from 'react-i18next'
import Moment from 'react-moment'
export default class Table extends React.Component {
constructor ({ scanResults }) {
class Table extends Component {
constructor ({ scanResults, t }) {
super()
this.scanResults = scanResults
? scanResults.map(row => ({
...row,
id: row._id
}))
: []
this.columns = [
{
field: 'name',
headerName: t('name'),
flex: 2,
renderCell: (cellValues) => {
return (
<Box
component='span'
sx={{
display: 'block'
}}
>
<Link
target='_blank'
href={cellValues.row.url}
rel='noreferrer'
>
{cellValues.row.fullName}
</Link>
<Typography variant='caption' display='block'>
<Moment
format='DD.MM HH:mm'
>
{cellValues.row.updatedAt}
</Moment>
</Typography>
</Box>
)
}
},
{
field: 'partsPrice',
headerName: t('parts_price'),
flex: 1
},
{
field: 'setPrice',
headerName: t('set_price'),
flex: 1
},
{
field: 'difference',
headerName: t('difference'),
flex: 1
}
]
}
static getInitialProps () {
return {
props: { scanResults: [] }
}
}
render () {
return (
<table>
<thead />
<tbody>
{this.scanResults.map((scanResult) =>
<ItemRow
key={scanResult._id}
scanResult={scanResult}
/>
)}
</tbody>
</table>
<Box
sx={{
height: '90vh',
width: '100%'
}}
>
<DataGrid
rows={this.scanResults}
columns={this.columns}
autoPageSize
initialState={{
sorting: {
sortModel: [{ field: 'difference', sort: 'desc' }]
}
}}
sx={{
borderLeft: 'none',
borderRight: 'none'
}}
/>
</Box>
)
}
}
export default withTranslation('home')(Table)

View File

@@ -1,41 +1,30 @@
import { Component } from 'react'
import dbConnect from '../../lib/dbConnect'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { models } from 'shared-stuff'
import dbConnect from '../../lib/dbConnect'
import Hero from './Hero'
import Table from './Table'
export default class Home extends Component {
constructor ({ scanResults }) {
super()
this.scanResults = scanResults
}
render () {
return (
<>
<Hero />
<Table
scanResults={this.scanResults}
/>
</>
)
}
export default function Home ({ scanResults }) {
return (
<>
<Hero />
<Table
scanResults={scanResults}
/>
</>
)
}
export async function getServerSideProps () {
try {
await dbConnect()
const scanResults = await models.ScanResult.find({})
export async function getServerSideProps ({ locale }) {
await dbConnect()
const scanResults = await models.ScanResult.find({})
return {
props: {
scanResults: JSON.parse(JSON.stringify(scanResults))
}
}
} catch (e) {
console.error(e)
return {
props: {}
return {
props: {
scanResults: JSON.parse(JSON.stringify(scanResults)),
...(await serverSideTranslations(locale, ['common', 'home']))
}
}
}

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

View File

@@ -0,0 +1,11 @@
{
"description": "Find a profitable difference between the price of the set and the price of the sum of it's parts.",
"help_header": "How to use this site",
"help_body_1": "The main page of the site has a table, that contains info about prime items, that is updated in real time. The column \"Name\" has a link to the item on warframe.market and the time of the last update.",
"help_body_2": "Knowing the difference between the price of the set and the price of the sum of it's parts lets you save platinum buying or even make a profit by purchasing the parts and selling them as a set.",
"help_body_3": "Have fun!",
"name": "Name",
"parts_price": "Parts Price",
"set_price": "Set Price",
"difference": "Difference"
}

View File

View File

@@ -0,0 +1,11 @@
{
"description": "Находи выгодную разницу между ценой набора и ценой его частей по отдельности.",
"help_header": "Как использовать этот сайт?",
"help_body_1": "На главной странице сайта находится таблица, которая содержит информацию о прайм предметах, обновляемую в реальном времени. Колонка \"Наименование\" содержит ссылку на страницу предмета на warframe.market и время последнего обновления информации.",
"help_body_2": "Зная разницу между ценой частей и ценой комплекта можно, например сэкономить на покупке или даже заработать, если купить части по отдельности, а продать как сет.",
"help_body_3": "Приятного использования!",
"name": "Наименование",
"parts_price": "Цена частей",
"set_price": "Цена набора",
"difference": "Разница"
}

View File

@@ -1 +0,0 @@
$clr-text: #1f1f1f;

View File

@@ -2,21 +2,21 @@ $text-font: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue,
sans-serif;
$font-h0: normal normal bold 56px/64px $text-font;
$font-h1: normal normal bold 48px/56px $text-font;
$font-h2: normal normal bold 40px/48px $text-font;
$font-h3: normal normal bold 36px/44px $text-font;
$font-h4: normal normal bold 32px/40px $text-font;
$font-h5: normal normal bold 28px/36px $text-font;
$font-h6: normal normal bold 24px/32px $text-font;
$font-h7: normal normal bold 20px/26px $text-font;
$font-h8: normal normal bold 18px/24px $text-font;
$font-h9: normal normal bold 16px/20px $text-font;
$font-h0: normal normal 300 56px/64px $text-font;
$font-h1: normal normal 300 48px/56px $text-font;
$font-h2: normal normal 300 40px/48px $text-font;
$font-h3: normal normal 300 36px/44px $text-font;
$font-h4: normal normal 300 32px/40px $text-font;
$font-h5: normal normal 300 28px/36px $text-font;
$font-h6: normal normal 300 24px/32px $text-font;
$font-h7: normal normal 300 20px/26px $text-font;
$font-h8: normal normal 300 18px/24px $text-font;
$font-h9: normal normal 300 16px/20px $text-font;
$font-r8: normal normal normal 24px/30px $text-font;
$font-r7: normal normal normal 22px/28px $text-font;
$font-r6: normal normal normal 20px/26px $text-font;
$font-r5: normal normal normal 18px/24px $text-font;
$font-r4: normal normal normal 16px/22px $text-font;
$font-r3: normal normal normal 14px/18px $text-font;
$font-r2: normal normal normal 12px/16px $text-font;
$font-r8: normal normal 300 24px/30px $text-font;
$font-r7: normal normal 300 22px/28px $text-font;
$font-r6: normal normal 300 20px/26px $text-font;
$font-r5: normal normal 300 18px/24px $text-font;
$font-r4: normal normal 300 16px/22px $text-font;
$font-r3: normal normal 300 14px/18px $text-font;
$font-r2: normal normal 300 12px/16px $text-font;

View File

@@ -1,2 +1 @@
@import './fonts';
@import './colors';

View File

@@ -5,7 +5,6 @@ body {
padding: 0;
margin: 0;
font: $font-r4;
color: $clr-text;
}
* {

9
gather/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
FROM node:20-alpine
LABEL authors="anatolykopyl"
WORKDIR /usr/node/app
COPY package*.json ./
RUN npm ci
COPY . .

View File

@@ -0,0 +1,9 @@
version: '3'
services:
warframe-center-gather:
image: git.radner.ru/anatolykopyl/warframe-center-gather:latest
container_name: warframe-center-gather
working_dir: /usr/node/app
command: >
sh -c "node src/index.js"

View File

@@ -1,9 +0,0 @@
module.exports = {
apps: [{
name: 'warframe-market-bot',
script: './src/index.js',
watch: true,
ignore_watch: ['node_modules', 'public'],
restart_delay: 1 * 60 * 1000
}]
}

View File

@@ -15,7 +15,6 @@
"shared-stuff": "file:../shared-stuff"
},
"devDependencies": {
"eslint-config-standard": "^16.0.3",
"pm2": "^5.2.0"
"eslint-config-standard": "^16.0.3"
}
}

View File

@@ -10,7 +10,16 @@ class Api {
async _get (url) {
await this.limiter.removeTokens(1)
return axios.get(url)
return axios.get(url).catch((error) => {
console.error(error)
return {
data: {
payload: {
orders: []
}
}
}
})
}
async getOrders (url) {
@@ -22,6 +31,10 @@ class Api {
async getSortedOrders (url) {
const orders = await this.getOrders(url)
if (orders.length === 0) {
return []
}
return orders.sort(function (a, b) {
return a.platinum - b.platinum
})

View File

@@ -7,25 +7,44 @@ async function initDB () {
await mongoose.connect(process.env.MONGODB_URI)
}
(async () => {
await initDB()
const main = async (firstLaunch) => {
if (firstLaunch) {
await initDB()
}
for (const item of items) {
console.log(`Looking at ${item.name}`)
process.stdout.write(`Looking at ${item.name}: `)
let partsPrice = 0
for (const part of item.parts) {
process.stdout.write(`${part.part} `)
partsPrice += await part.getPrice()
}
const setPrice = await item.set.getPrice()
if (partsPrice < setPrice) {
models.ScanResult.findOneAndUpdate(
{ name: item.name },
{ partsPrice, setPrice },
{ upsert: true },
() => {}
)
}
await models.ScanResult.findOneAndUpdate(
{ name: item.name },
{
fullName: item.fullName,
url: item.set.url,
timestamp: new Date(),
partsPrice,
setPrice,
difference: setPrice - partsPrice
},
{ upsert: true }
)
console.log('✅')
}
})()
await main()
}
main(true)
process.on('SIGINT', () => {
mongoose.disconnect().then(() => {
process.exit()
})
})

View File

@@ -1,8 +1,13 @@
const Part = require('./Part')
function capitalize (string) {
return string.charAt(0).toUpperCase() + string.slice(1)
}
module.exports = class Item {
constructor (name) {
this.name = name
this.fullName = capitalize(name) + ' Prime'
this.set = new Part(name, 'set')
this.parts = []

View File

@@ -3,12 +3,17 @@ const Api = require('../api')
module.exports = class Part {
constructor (set, part, amount) {
this.part = part
this.url = `${set}_prime_${part}`
this.urlPath = `${set}_prime_${part}`
this.url = `https://warframe.market/items/${set}_prime_${part}`
this.amount = amount ?? 1
}
async getPrice () {
const orders = await Api.getSortedOrders(this.url)
const orders = await Api.getSortedOrders(this.urlPath)
if (orders.length === 0) {
return 0
}
return Number(orders[0].platinum) * this.amount
}
}

10553
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,5 +7,12 @@
"url": "ssh://git@git.radner.ru:3036/anatolykopyl/warframe-center.git"
},
"author": "Anatoly Kopyl",
"workspaces": ["./app", "./gather", "./shared-stuff"]
"workspaces": [
"./app",
"./gather",
"./shared-stuff"
],
"dependencies": {
"mongoose": "^6.2.7"
}
}

11
shared-stuff/dist/index.js vendored Normal file
View File

@@ -0,0 +1,11 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.models = void 0;
const ScanResult_1 = __importDefault(require("./models/ScanResult"));
const models = {
ScanResult: ScanResult_1.default
};
exports.models = models;

17
shared-stuff/dist/models/ScanResult.js vendored Normal file
View File

@@ -0,0 +1,17 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const mongoose_1 = __importDefault(require("mongoose"));
const scanResultSchema = new mongoose_1.default.Schema({
name: String,
fullName: String,
url: String,
partsPrice: Number,
setPrice: Number,
difference: Number
}, {
timestamps: true
});
exports.default = mongoose_1.default.models.ScanResult || mongoose_1.default.model('ScanResult', scanResultSchema);

View File

@@ -2,8 +2,14 @@ const mongoose = require('mongoose')
const scanResultSchema = new mongoose.Schema({
name: String,
fullName: String,
url: String,
partsPrice: Number,
setPrice: Number
setPrice: Number,
difference: Number
},
{
timestamps: true
})
module.exports = mongoose.models.ScanResult || mongoose.model('ScanResult', scanResultSchema)