├── bitimulate-backend ├── .gitignore ├── src │ ├── crawler │ │ ├── config.json │ │ └── worker.js │ ├── lib │ │ ├── common │ │ │ ├── index.js │ │ │ ├── parseJSON.js │ │ │ ├── polyfill.js │ │ │ └── getExchangeRate.js │ │ ├── middlewares │ │ │ ├── needAuth.js │ │ │ └── jwt.js │ │ ├── poloniex │ │ │ ├── currencyPairs.js │ │ │ ├── currencyPairMap.js │ │ │ └── index.js │ │ ├── variables.js │ │ ├── log.js │ │ ├── token.js │ │ ├── social.js │ │ └── cache.js │ ├── api │ │ ├── v1.0 │ │ │ ├── wallet │ │ │ │ ├── index.js │ │ │ │ └── wallet.ctrl.js │ │ │ ├── chartData │ │ │ │ └── index.js │ │ │ ├── exchange │ │ │ │ ├── index.js │ │ │ │ └── exchange.ctrl.js │ │ │ ├── common │ │ │ │ ├── index.js │ │ │ │ └── common.ctrl.js │ │ │ ├── orders │ │ │ │ └── index.js │ │ │ ├── user │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ └── auth │ │ │ │ └── index.js │ │ └── index.js │ ├── trader │ │ └── index.js │ ├── worker │ │ ├── processManually.js │ │ └── index.js │ ├── ws │ │ └── utils.js │ └── db │ │ ├── index.js │ │ └── models │ │ ├── EarningsHistory.js │ │ ├── Order.js │ │ ├── ChartData.js │ │ └── ExchangeRate.js ├── jsconfig.json ├── README.md ├── deploy.config.json ├── .env.bak ├── .eslintrc.js └── workers.config.json └── bitimulate-frontend ├── src ├── lib │ ├── hoc │ │ ├── redirectWhenUnauthorized.js │ │ └── scuize.js │ ├── api │ │ ├── exchange.js │ │ ├── chartData.js │ │ ├── report.js │ │ ├── common.js │ │ ├── user.js │ │ ├── orders.js │ │ ├── poloniex.js │ │ └── auth.js │ ├── errors.js │ ├── storage.js │ ├── social.js │ ├── variables.js │ └── aggregateWallet.js ├── styles │ ├── main.scss │ ├── utils.scss │ ├── lib │ │ ├── functions │ │ │ ├── _all.scss │ │ │ └── _common.scss │ │ ├── mixins │ │ │ ├── _all.scss │ │ │ ├── _common.scss │ │ │ ├── _responsive.scss │ │ │ └── _material-shadow.scss │ │ ├── _all.scss │ │ ├── _variables.scss │ │ └── _keyframes.scss │ └── base │ │ ├── _all.scss │ │ ├── _typography.scss │ │ └── _reset.scss ├── components │ ├── atoms │ │ ├── BgColor │ │ │ ├── BgColor.scss │ │ │ ├── index.js │ │ │ └── BgColor.js │ │ ├── CoinIcon │ │ │ ├── CoinIcon.scss │ │ │ └── index.js │ │ ├── ResponsiveAd │ │ │ ├── ResponsiveAd.scss │ │ │ ├── index.js │ │ │ └── ResponsiveAd.js │ │ ├── Logo │ │ │ ├── index.js │ │ │ ├── Logo.scss │ │ │ └── Logo.js │ │ ├── Block │ │ │ ├── index.js │ │ │ ├── Block.scss │ │ │ └── Block.js │ │ ├── Button │ │ │ ├── index.js │ │ │ └── Button.js │ │ ├── Dimmer │ │ │ ├── index.js │ │ │ ├── Dimmer.scss │ │ │ └── Dimmer.js │ │ ├── FlexBox │ │ │ ├── index.js │ │ │ ├── FlexBox.scss │ │ │ └── FlexBox.js │ │ ├── Input │ │ │ ├── index.js │ │ │ ├── Input.js │ │ │ └── Input.scss │ │ ├── Modal │ │ │ ├── index.js │ │ │ ├── Modal.scss │ │ │ └── Modal.js │ │ ├── NavItem │ │ │ ├── index.js │ │ │ ├── NavItem.scss │ │ │ └── NavItem.js │ │ ├── Option │ │ │ ├── index.js │ │ │ ├── Option.js │ │ │ └── Option.scss │ │ ├── Paper │ │ │ ├── index.js │ │ │ ├── Paper.js │ │ │ └── Paper.scss │ │ ├── Spinner │ │ │ ├── index.js │ │ │ ├── Spinner.scss │ │ │ └── Spinner.js │ │ ├── Hamburger │ │ │ ├── index.js │ │ │ ├── Hamburger.js │ │ │ └── Hamburger.scss │ │ ├── LoadMore │ │ │ ├── index.js │ │ │ ├── LoadMore.js │ │ │ └── LoadMore.scss │ │ ├── AlignRight │ │ │ ├── index.js │ │ │ ├── AlignRight.scss │ │ │ └── AlignRight.js │ │ ├── Fullscreen │ │ │ ├── index.js │ │ │ ├── Fullscreen.scss │ │ │ └── Fullscreen.js │ │ ├── InputError │ │ │ ├── index.js │ │ │ ├── InputError.scss │ │ │ └── InputError.js │ │ ├── ScreenMask │ │ │ ├── index.js │ │ │ ├── ScreenMask.scss │ │ │ └── ScreenMask.js │ │ ├── TextButton │ │ │ ├── index.js │ │ │ ├── TextButton.js │ │ │ └── TextButton.scss │ │ ├── UserButton │ │ │ ├── index.js │ │ │ ├── UserButton.js │ │ │ └── UserButton.scss │ │ ├── IntroQuestion │ │ │ ├── index.js │ │ │ ├── IntroQuestion.js │ │ │ └── IntroQuestion.scss │ │ ├── PolyBackground │ │ │ ├── index.js │ │ │ ├── PolyBackground.scss │ │ │ └── PolyBackground.js │ │ ├── SelectCurrency │ │ │ ├── index.js │ │ │ ├── SelectCurrency.js │ │ │ └── SelectCurrency.scss │ │ ├── SidebarWrapper │ │ │ ├── index.js │ │ │ ├── SidebarWrapper.js │ │ │ └── SidebarWrapper.scss │ │ ├── SectionWithTitle │ │ │ ├── SectionWithTitle.scss │ │ │ ├── index.js │ │ │ └── SectionWithTitle.js │ │ ├── SocialLoginButton │ │ │ ├── index.js │ │ │ ├── SocialLoginButton.js │ │ │ └── SocialLoginButton.scss │ │ ├── Card │ │ │ ├── index.js │ │ │ ├── Card.scss │ │ │ └── Card.js │ │ ├── Selector │ │ │ ├── index.js │ │ │ └── Selector.scss │ │ ├── HoverCard │ │ │ ├── index.js │ │ │ ├── HoverCard.scss │ │ │ └── HoverCard.js │ │ ├── LabelBlock │ │ │ ├── index.js │ │ │ ├── LabelBlock.scss │ │ │ └── LabelBlock.js │ │ ├── OrdersTable │ │ │ ├── index.js │ │ │ └── OrdersTable.scss │ │ ├── SortReverser │ │ │ ├── index.js │ │ │ ├── SortReverser.scss │ │ │ └── SortReverser.js │ │ └── ButtonSelector │ │ │ ├── index.js │ │ │ ├── ButtonSelector.scss │ │ │ └── ButtonSelector.js │ ├── pages │ │ ├── WalletHistorySubpage │ │ │ ├── WalletHistorySubpage.scss │ │ │ ├── index.js │ │ │ └── WalletHistorySubpage.js │ │ ├── WalletProfitSubpage │ │ │ ├── WalletProfitSubpage.scss │ │ │ ├── index.js │ │ │ └── WalletProfitSubpage.js │ │ ├── HomePage │ │ │ └── index.js │ │ ├── TermsPage │ │ │ ├── index.js │ │ │ └── TermsPage.scss │ │ ├── TradePage │ │ │ ├── index.js │ │ │ ├── TradePage.scss │ │ │ └── TradePage.js │ │ ├── RankingPage │ │ │ ├── index.js │ │ │ ├── RankingPage.js │ │ │ └── RankingPage.scss │ │ ├── RegisterPage │ │ │ ├── index.js │ │ │ ├── RegisterPage.scss │ │ │ └── RegisterPage.js │ │ ├── ReportPage │ │ │ ├── index.js │ │ │ ├── ReportPage.scss │ │ │ └── ReportPage.js │ │ ├── RewardPage │ │ │ ├── index.js │ │ │ ├── RewardPage.scss │ │ │ └── RewardPage.js │ │ ├── WalletPage │ │ │ ├── index.js │ │ │ ├── WalletPage.scss │ │ │ └── WalletPage.js │ │ ├── WalletSubpage │ │ │ ├── WalletSubpage.scss │ │ │ ├── index.js │ │ │ └── WalletSubpage.js │ │ ├── TradeIndexSubpage │ │ │ ├── TradeIndexSubpage.scss │ │ │ ├── index.js │ │ │ └── TradeIndexSubpage.js │ │ └── TradeDetailSubpage │ │ │ ├── index.js │ │ │ ├── TradeDetailSubpage.scss │ │ │ └── TradeDetailSubpage.js │ ├── molecules │ │ ├── HeaderNav │ │ │ ├── HeaderNav.scss │ │ │ ├── index.js │ │ │ └── HeaderNav.js │ │ ├── Msgbox │ │ │ ├── index.js │ │ │ ├── Msgbox.js │ │ │ └── Msgbox.scss │ │ ├── Ranking │ │ │ ├── index.js │ │ │ └── Ranking.scss │ │ ├── CoinBlock │ │ │ ├── index.js │ │ │ ├── CoinBlock.js │ │ │ └── CoinBlock.scss │ │ ├── ProfitInfo │ │ │ ├── index.js │ │ │ └── ProfitInfo.scss │ │ ├── TradeChart │ │ │ ├── index.js │ │ │ └── TradeChart.scss │ │ ├── UserMenu │ │ │ ├── index.js │ │ │ └── UserMenu.scss │ │ ├── WalletMenu │ │ │ ├── index.js │ │ │ ├── WalletMenu.js │ │ │ └── WalletMenu.scss │ │ ├── ProfitChart │ │ │ ├── index.js │ │ │ └── ProfitChart.scss │ │ ├── SpinnerBlock │ │ │ ├── index.js │ │ │ ├── SpinnerBlock.scss │ │ │ └── SpinnerBlock.js │ │ ├── TradeHistory │ │ │ ├── index.js │ │ │ ├── TradeHistory.scss │ │ │ └── TradeHistory.js │ │ ├── TripleWallet │ │ │ ├── index.js │ │ │ ├── TripleWallet.js │ │ │ └── TripleWallet.scss │ │ ├── WalletTable │ │ │ ├── index.js │ │ │ └── WalletTable.scss │ │ ├── DimmerSpinner │ │ │ ├── index.js │ │ │ └── DimmerSpinner.js │ │ ├── TradeHistoryTable │ │ │ └── index.js │ │ ├── InitialMoneyOptions │ │ │ ├── index.js │ │ │ └── InitialMoneyOptions.js │ │ ├── TradeBox │ │ │ └── index.js │ │ ├── OrderBook │ │ │ ├── index.js │ │ │ ├── OrderBook.scss │ │ │ └── OrderBook.js │ │ ├── CurrentInfo │ │ │ ├── index.js │ │ │ ├── CurrentInfo.scss │ │ │ └── CurrentInfo.js │ │ ├── RateInfoCard │ │ │ └── index.js │ │ ├── BitcoinInfoCard │ │ │ ├── index.js │ │ │ └── BitcoinInfoCard.scss │ │ ├── HorizontalLabelInput │ │ │ ├── index.js │ │ │ ├── HorizontalLabelInput.js │ │ │ └── HorizontalLabelInput.scss │ │ └── TradeIndexOptions │ │ │ ├── index.js │ │ │ ├── TradeIndexOptions.scss │ │ │ └── TradeIndexOptions.js │ ├── organisms │ │ ├── Header │ │ │ ├── index.js │ │ │ ├── Header.scss │ │ │ └── Header.js │ │ ├── Sidebar │ │ │ ├── index.js │ │ │ ├── Sidebar.scss │ │ │ └── Sidebar.js │ │ ├── Wallets │ │ │ ├── index.js │ │ │ ├── Wallets.scss │ │ │ └── Wallets.js │ │ ├── CoinMain │ │ │ ├── index.js │ │ │ ├── CoinMain.scss │ │ │ └── CoinMain.js │ │ ├── LoginModal │ │ │ ├── index.js │ │ │ └── LoginModal.scss │ │ ├── TradeIndex │ │ │ ├── index.js │ │ │ ├── TradeIndex.scss │ │ │ └── TradeIndex.js │ │ ├── RegisterForm │ │ │ ├── index.js │ │ │ └── RegisterForm.scss │ │ ├── RewardWalletForm │ │ │ ├── index.js │ │ │ ├── RewardWalletForm.scss │ │ │ └── RewardWalletForm.js │ │ └── TradeSection │ │ │ ├── index.js │ │ │ └── TradeSection.scss │ ├── templates │ │ ├── PageTemplate │ │ │ ├── index.js │ │ │ ├── PageTemplate.scss │ │ │ └── PageTemplate.js │ │ ├── ReportTemplate │ │ │ ├── index.js │ │ │ ├── ReportTemplate.scss │ │ │ └── ReportTemplate.js │ │ └── RegisterTemplate │ │ │ ├── index.js │ │ │ ├── RegisterTemplate.scss │ │ │ └── RegisterTemplate.js │ ├── index.js │ └── App.js ├── store │ ├── index.js │ ├── modules │ │ ├── index.js │ │ ├── ranking.js │ │ └── common.js │ └── configure.js ├── static │ ├── images │ │ ├── logo.png │ │ └── background.png │ └── fonts │ │ └── NanumSquare │ │ └── fonts │ │ ├── NanumSquareB.eot │ │ ├── NanumSquareB.ttf │ │ ├── NanumSquareL.eot │ │ ├── NanumSquareL.ttf │ │ ├── NanumSquareR.eot │ │ ├── NanumSquareR.ttf │ │ ├── NanumSquareB.woff │ │ ├── NanumSquareEB.eot │ │ ├── NanumSquareEB.ttf │ │ ├── NanumSquareEB.woff │ │ ├── NanumSquareL.woff │ │ └── NanumSquareR.woff ├── containers │ ├── index.js │ ├── SocketSubscriber.js │ ├── ScreenMaskContainer.js │ ├── CoinMainContainer.js │ ├── Core.js │ ├── MsgboxContainer.js │ ├── IntroQuestionContainer.js │ ├── RankingContainer.js │ └── UserLoader.js ├── Root.js └── index.js ├── README.md ├── public ├── favicon.ico ├── ogimage.png ├── redirect.html └── manifest.json ├── jsconfig.json ├── .storybook ├── webpack.config.js ├── addons.js └── config.js ├── .gitignore ├── config ├── jest │ ├── fileTransform.js │ └── cssTransform.js └── polyfills.js └── scripts └── test.js /bitimulate-backend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/lib/hoc/redirectWhenUnauthorized.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/styles/main.scss: -------------------------------------------------------------------------------- 1 | @import 'base/all'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/BgColor/BgColor.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/CoinIcon/CoinIcon.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/styles/utils.scss: -------------------------------------------------------------------------------- 1 | @import 'lib/all'; 2 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/styles/lib/functions/_all.scss: -------------------------------------------------------------------------------- 1 | @import 'common'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/ResponsiveAd/ResponsiveAd.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bitimulate-backend/src/crawler/config.json: -------------------------------------------------------------------------------- 1 | {"lastUpdatedDate":1503935079173} 2 | -------------------------------------------------------------------------------- /bitimulate-frontend/README.md: -------------------------------------------------------------------------------- 1 | # bitimulate-frontend 2 | 3 | To be implemented.. -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Logo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Logo'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/BgColor/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './BgColor'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Block/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Block'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Button/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Button'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Dimmer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Dimmer'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/FlexBox/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './FlexBox'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Input/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Input'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Modal/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Modal'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/NavItem/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './NavItem'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Option/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Option'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Paper/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Paper'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Spinner/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Spinner'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/WalletHistorySubpage/WalletHistorySubpage.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/WalletProfitSubpage/WalletProfitSubpage.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/styles/base/_all.scss: -------------------------------------------------------------------------------- 1 | @import 'typography'; 2 | @import 'reset'; 3 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/CoinIcon/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './CoinIcon'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Hamburger/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Hamburger'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/LoadMore/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './LoadMore'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/HeaderNav/HeaderNav.scss: -------------------------------------------------------------------------------- 1 | .header-nav { 2 | 3 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/Msgbox/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Msgbox'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/Ranking/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Ranking'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/Header/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Header'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/Sidebar/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Sidebar'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/Wallets/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Wallets'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/HomePage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './HomePage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/TermsPage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './TermsPage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/TradePage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './TradePage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/AlignRight/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './AlignRight'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Fullscreen/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Fullscreen'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/InputError/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './InputError'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/ResponsiveAd/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './ResponsiveAd'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/ScreenMask/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './ScreenMask'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/TextButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './TextButton'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/UserButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './UserButton'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/CoinBlock/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './CoinBlock'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/HeaderNav/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './HeaderNav'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/ProfitInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './ProfitInfo'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/TradeChart/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './TradeChart'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/UserMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './UserMenu'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/WalletMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './WalletMenu'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/CoinMain/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './CoinMain'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/LoginModal/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './LoginModal'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/TradeIndex/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './TradeIndex'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/RankingPage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './RankingPage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/RegisterPage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './RegisterPage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/ReportPage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './ReportPage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/RewardPage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './RewardPage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/WalletPage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './WalletPage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/WalletSubpage/WalletSubpage.scss: -------------------------------------------------------------------------------- 1 | .wallet-subpage { 2 | 3 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/IntroQuestion/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './IntroQuestion'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/PolyBackground/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './PolyBackground'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SelectCurrency/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './SelectCurrency'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SidebarWrapper/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './SidebarWrapper'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/ProfitChart/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './ProfitChart'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/SpinnerBlock/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './SpinnerBlock'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/TradeHistory/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './TradeHistory'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/TripleWallet/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './TripleWallet'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/WalletTable/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './WalletTable'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/RegisterForm/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './RegisterForm'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/WalletSubpage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './WalletSubpage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/templates/PageTemplate/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './PageTemplate'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SectionWithTitle/SectionWithTitle.scss: -------------------------------------------------------------------------------- 1 | .section-with-title { 2 | 3 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SectionWithTitle/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './SectionWithTitle'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SocialLoginButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './SocialLoginButton'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/DimmerSpinner/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './DimmerSpinner'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/TradeIndexSubpage/TradeIndexSubpage.scss: -------------------------------------------------------------------------------- 1 | .trade-index-subpage { 2 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/TradeIndexSubpage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './TradeIndexSubpage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/templates/ReportTemplate/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './ReportTemplate'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Card/index.js: -------------------------------------------------------------------------------- 1 | import Card from './Card.js' 2 | 3 | export default Card 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/TradeHistoryTable/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './TradeHistoryTable'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/RewardWalletForm/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './RewardWalletForm'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/TradeDetailSubpage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './TradeDetailSubpage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/WalletProfitSubpage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './WalletProfitSubpage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/templates/RegisterTemplate/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './RegisterTemplate'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/InitialMoneyOptions/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './InitialMoneyOptions'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/TradeDetailSubpage/TradeDetailSubpage.scss: -------------------------------------------------------------------------------- 1 | .ad-area { 2 | margin-top: 1rem; 3 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/WalletHistorySubpage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './WalletHistorySubpage'; -------------------------------------------------------------------------------- /bitimulate-frontend/src/store/index.js: -------------------------------------------------------------------------------- 1 | import configureStore from './configure'; 2 | 3 | export default configureStore(); -------------------------------------------------------------------------------- /bitimulate-frontend/src/styles/lib/mixins/_all.scss: -------------------------------------------------------------------------------- 1 | @import 'material-shadow'; 2 | @import 'responsive'; 3 | @import 'common'; -------------------------------------------------------------------------------- /bitimulate-frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/public/favicon.ico -------------------------------------------------------------------------------- /bitimulate-frontend/public/ogimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/public/ogimage.png -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Selector/index.js: -------------------------------------------------------------------------------- 1 | import Selector from './Selector.js' 2 | 3 | export default Selector 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/styles/lib/functions/_common.scss: -------------------------------------------------------------------------------- 1 | @function z-index-for($key) { 2 | @return map-get($z-indexes, $key); 3 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/HoverCard/index.js: -------------------------------------------------------------------------------- 1 | import HoverCard from './HoverCard.js' 2 | 3 | export default HoverCard 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/TradeBox/index.js: -------------------------------------------------------------------------------- 1 | import TradeBox from './TradeBox.js' 2 | 3 | export default TradeBox 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/LabelBlock/index.js: -------------------------------------------------------------------------------- 1 | import LabelBlock from './LabelBlock.js' 2 | 3 | export default LabelBlock 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/OrderBook/index.js: -------------------------------------------------------------------------------- 1 | import OrderBook from './OrderBook.js' 2 | 3 | export default OrderBook 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/OrdersTable/index.js: -------------------------------------------------------------------------------- 1 | import OrdersTable from './OrdersTable.js' 2 | 3 | export default OrdersTable 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SortReverser/index.js: -------------------------------------------------------------------------------- 1 | import SortReverser from './SortReverser.js' 2 | 3 | export default SortReverser 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Spinner/Spinner.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .spinner { 4 | color: material-color('grey', '300'); 5 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/CurrentInfo/index.js: -------------------------------------------------------------------------------- 1 | import CurrentInfo from './CurrentInfo.js' 2 | 3 | export default CurrentInfo 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/Wallets/Wallets.scss: -------------------------------------------------------------------------------- 1 | .wallets { 2 | 3 | } 4 | 5 | section + section { 6 | margin-top: 3rem; 7 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/lib/api/exchange.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const getInitialRate = () => axios.get('/api/v1.0/exchange/'); -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/images/logo.png -------------------------------------------------------------------------------- /bitimulate-backend/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src" // all paths are relative to the baseUrl 4 | } 5 | } -------------------------------------------------------------------------------- /bitimulate-frontend/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src" // all paths are relative to the baseUrl 4 | } 5 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/RateInfoCard/index.js: -------------------------------------------------------------------------------- 1 | import RateInfoCard from './RateInfoCard.js' 2 | 3 | export default RateInfoCard 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/TradeSection/index.js: -------------------------------------------------------------------------------- 1 | import TradeSection from './TradeSection.js' 2 | 3 | export default TradeSection 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/TradePage/TradePage.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .ad-area { 4 | width: 100%; 5 | margin-top: 1rem; 6 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/AlignRight/AlignRight.scss: -------------------------------------------------------------------------------- 1 | .align-right { 2 | display: flex; 3 | .inner { 4 | margin-left: auto; 5 | } 6 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/ButtonSelector/index.js: -------------------------------------------------------------------------------- 1 | import ButtonSelector from './ButtonSelector.js' 2 | 3 | export default ButtonSelector 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/images/background.png -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/BitcoinInfoCard/index.js: -------------------------------------------------------------------------------- 1 | import BitcoinInfoCard from './BitcoinInfoCard.js' 2 | 3 | export default BitcoinInfoCard 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/styles/base/_typography.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Noto+Serif:700i'); 2 | @import url('static/fonts/NanumSquare/font.css') -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/HorizontalLabelInput/index.js: -------------------------------------------------------------------------------- 1 | import HorizontalLabelInput from './HorizontalLabelInput.js' 2 | 3 | export default HorizontalLabelInput 4 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/CoinMain/CoinMain.scss: -------------------------------------------------------------------------------- 1 | .coin-main { 2 | display: flex; 3 | flex-wrap: wrap; 4 | margin-left: -0.5rem; 5 | margin-right: -0.5rem; 6 | } -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/common/index.js: -------------------------------------------------------------------------------- 1 | exports.parseJSON = require('./parseJSON'); 2 | exports.polyfill = require('./polyfill'); 3 | exports.getExchangeRate = require('./getExchangeRate'); -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareB.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareB.eot -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareB.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareB.ttf -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareL.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareL.eot -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareL.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareL.ttf -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareR.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareR.eot -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareR.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareR.ttf -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Logo/Logo.scss: -------------------------------------------------------------------------------- 1 | .logo { 2 | font-family: 'Noto Serif', serif; 3 | font-size: 1.5rem; 4 | color: white; 5 | user-select: none; 6 | cursor: pointer; 7 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/lib/errors.js: -------------------------------------------------------------------------------- 1 | const errors = { 2 | 'INVALID_PAIR': '허용되지 않는 화폐입니다.', 3 | 'EXCEED_AMOUNT': '자산이 부족합니다.', 4 | 'BAD_REQUEST': '잘못된 요청입니다.' 5 | } 6 | 7 | export default errors; -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareB.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareB.woff -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareEB.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareEB.eot -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareEB.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareEB.ttf -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareEB.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareEB.woff -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareL.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareL.woff -------------------------------------------------------------------------------- /bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareR.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/velopert/bitimulate/HEAD/bitimulate-frontend/src/static/fonts/NanumSquare/fonts/NanumSquareR.woff -------------------------------------------------------------------------------- /bitimulate-backend/README.md: -------------------------------------------------------------------------------- 1 | # bitimulate-backend 2 | 3 | ## Stack 4 | - Node.js 5 | - Koa 6 | - Mongoose 7 | - Websocket 8 | - MongoDB 9 | 10 | > More information will be documented later on. -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/RegisterPage/RegisterPage.scss: -------------------------------------------------------------------------------- 1 | .description { 2 | color: #757575; 3 | white-space: pre-wrap; 4 | margin-bottom: 0.5rem; 5 | } 6 | 7 | .register-button { 8 | 9 | } -------------------------------------------------------------------------------- /bitimulate-frontend/.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const config = require('../config/webpack.config.dev.js'); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | module: { 6 | rules: config.module.rules 7 | } 8 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/HoverCard/HoverCard.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .hover-card { 4 | background: white; 5 | transition: .3s ease-in; 6 | &:hover { 7 | @include material-shadow(4, 0.5); 8 | } 9 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/TermsPage/TermsPage.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .document { 4 | width: 1024px; 5 | @include responsive(); 6 | margin-top: 2rem; 7 | margin-bottom: 2rem; 8 | display: block; 9 | } -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/middlewares/needAuth.js: -------------------------------------------------------------------------------- 1 | module.exports = (ctx, next) => { 2 | const { user } = ctx.request; 3 | if(!user) { 4 | ctx.status = 401; // Unauthorized 5 | return null; 6 | } 7 | return next(); 8 | }; -------------------------------------------------------------------------------- /bitimulate-frontend/public/redirect.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Card/Card.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .card { 4 | background: white; 5 | @include material-shadow(1, 0.5); 6 | padding: 1rem; 7 | &.noPadding { 8 | padding: 0; 9 | } 10 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/styles/lib/_all.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | @import 'keyframes'; 3 | @import 'mixins/all'; 4 | @import 'functions/all'; 5 | @import '~sass-material-colors'; 6 | @import '~include-media/dist/include-media'; 7 | -------------------------------------------------------------------------------- /bitimulate-frontend/.storybook/addons.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */ 2 | 3 | import '@storybook/addon-actions/register'; 4 | import '@storybook/addon-links/register'; 5 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/SpinnerBlock/SpinnerBlock.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | .spinner-block { 3 | display: flex; 4 | align-items: center; 5 | justify-content: center; 6 | color: material-color('cyan', '400'); 7 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/templates/RegisterTemplate/RegisterTemplate.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .register-template { 4 | p { 5 | color: material-color('grey', '600'); 6 | } 7 | > section { 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /bitimulate-backend/src/api/v1.0/wallet/index.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | 3 | const wallet = new Router(); 4 | const walletCtrl = require('./wallet.ctrl'); 5 | 6 | wallet.get('/', walletCtrl.get); 7 | 8 | module.exports = wallet; -------------------------------------------------------------------------------- /bitimulate-backend/deploy.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [{ 3 | "name": "server", 4 | "script" : "./src", 5 | // "instances": 2, 6 | // "exec_mode": "cluster", 7 | "env": { 8 | "NODE_PATH": "src" 9 | } 10 | }] 11 | } -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/common/parseJSON.js: -------------------------------------------------------------------------------- 1 | module.exports = function parseJSON(str) { 2 | let parsed = null; 3 | try { 4 | parsed = JSON.parse(str); 5 | } catch (e) { 6 | return null; 7 | } 8 | return parsed; 9 | }; 10 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/lib/api/chartData.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const getChartData = ({name, type, timebase}) => axios.get(`/api/v1.0/chart-data/${name}?type=${type}`, { 4 | headers: { 5 | 'x-timebase': timebase 6 | } 7 | }); -------------------------------------------------------------------------------- /bitimulate-backend/src/api/index.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const versions = { 3 | '1.0': require('./v1.0') 4 | }; 5 | 6 | const api = new Router(); 7 | 8 | api.use('/v1.0', versions['1.0'].routes()); 9 | 10 | module.exports = api; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Fullscreen/Fullscreen.scss: -------------------------------------------------------------------------------- 1 | .fullscreen { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | right: 0; 6 | bottom: 0; 7 | background: linear-gradient(to right bottom,#276caf,#30a1c1,#9dd5ba); 8 | } 9 | 10 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/TradeSection/TradeSection.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | .trade-section { 3 | display: flex; 4 | margin-top: 1rem; 5 | flex-direction: row; 6 | @include media(" { 4 | const containerName = key.replace(/^\.\/([^.]+)\.js$/, '$1') 5 | module.exports[containerName] = req(key).default 6 | }) -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/common/polyfill.js: -------------------------------------------------------------------------------- 1 | exports.objectWithoutProperties = (obj, keys) => { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } 2 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/lib/api/report.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const getUserWallet = (displayName) => axios.get(`/api/v1.0/user/${displayName}/wallet`); 4 | export const getUserOrders = (displayName) => axios.get(`/api/v1.0/user/${displayName}/orders`); -------------------------------------------------------------------------------- /bitimulate-backend/src/api/v1.0/chartData/index.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | 3 | const chartData = new Router(); 4 | const chartDataCtrl = require('./chartData.ctrl'); 5 | 6 | chartData.get('/:name', chartDataCtrl.getChartData); 7 | 8 | module.exports = chartData; -------------------------------------------------------------------------------- /bitimulate-backend/src/api/v1.0/exchange/index.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | 3 | const exchange = new Router(); 4 | const exchangeCtrl = require('./exchange.ctrl'); 5 | 6 | exchange.get('/', exchangeCtrl.getInitialExchangeRate); 7 | 8 | module.exports = exchange; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/templates/ReportTemplate/ReportTemplate.scss: -------------------------------------------------------------------------------- 1 | .report-template { 2 | min-height: 30rem; 3 | .ads-area { 4 | margin-top: 2rem; 5 | } 6 | .trade-history { 7 | margin-top: 3rem; 8 | h2 { 9 | margin: 0; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/styles/lib/mixins/_common.scss: -------------------------------------------------------------------------------- 1 | @mixin flex-center() { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | } 6 | 7 | @mixin hover() { 8 | @media (hover: hover) { 9 | &:hover { 10 | @content; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/variables.js: -------------------------------------------------------------------------------- 1 | exports.optionsPerCurrency = { 2 | 'KRW': { 3 | symbol: '₩', 4 | initialValue: 1000000 5 | }, 6 | 'USD': { 7 | symbol: '$', 8 | initialValue: 1000 9 | }, 10 | 'BTC': { 11 | symbol: 'Ƀ', 12 | initialValue: 1 13 | } 14 | }; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/FlexBox/FlexBox.scss: -------------------------------------------------------------------------------- 1 | .flex-box { 2 | display: flex; 3 | &.row { 4 | flex-direction: row; 5 | } 6 | &.column { 7 | flex-direction: column; 8 | } 9 | &.center { 10 | align-items: center; 11 | justify-content: center; 12 | } 13 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/styles/lib/mixins/_responsive.scss: -------------------------------------------------------------------------------- 1 | @mixin responsive() { 2 | width: 1200px; 3 | margin: 0 auto; 4 | transition: width .3s ease-in-out; 5 | @include media(" { 4 | try { 5 | const rates = await ExchangeRate.showAll(); 6 | ctx.body = rates; 7 | } catch (e) { 8 | ctx.throw(e, 500); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /bitimulate-backend/src/trader/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const log = require('lib/log'); 3 | const db = require('db'); 4 | const trader = require('./trader'); 5 | 6 | const initialize = async () => { 7 | await db.connect(); 8 | trader.beginSync(); 9 | }; 10 | 11 | initialize(); 12 | // sync current price every 1sec 13 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/styles/lib/_variables.scss: -------------------------------------------------------------------------------- 1 | $breakpoints: ( 2 | small: 376px, 3 | medium: 768px, 4 | large: 1024px, 5 | huge: 1200px 6 | ); 7 | 8 | $z-indexes: ( 9 | 'header': 5, 10 | 'screen-mask': 20, 11 | 'modal': 30, 12 | 'dimmer': 35, 13 | 'user-menu': 10, 14 | 'sidebar': 10, 15 | 'hamburger': 15 16 | ); -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Dimmer/Dimmer.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .dimmer { 4 | position: fixed; 5 | top: 0; 6 | left: 0; 7 | right: 0; 8 | bottom: 0; 9 | background: rgba(0,0,0,0.5); 10 | 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | z-index: z-index-for('modal'); 15 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/index.js: -------------------------------------------------------------------------------- 1 | // https://github.com/diegohaz/arc/wiki/Atomic-Design#do-not-worry 2 | const req = require.context('.', true, /\.\/[^/]+\/[^/]+\/index\.js$/) 3 | 4 | req.keys().forEach((key) => { 5 | const componentName = key.replace(/^.+\/([^/]+)\/index\.js/, '$1') 6 | module.exports[componentName] = req(key).default 7 | }) -------------------------------------------------------------------------------- /bitimulate-backend/src/worker/processManually.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const db = require('db'); 3 | const processWallets = require('./processWallets'); 4 | require('mongoose').set('debug', true); 5 | db.connect(); 6 | 7 | async function initialize() { 8 | await db.connect(); 9 | await processWallets(); 10 | } 11 | 12 | initialize(); -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/CurrentInfo/CurrentInfo.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .current-info { 4 | margin-top: 1rem; 5 | background: material-color('grey', '200'); 6 | @include material-shadow(1, 0.5); 7 | padding: 1rem; 8 | display: flex; 9 | flex-direction: row; 10 | @include media(" { 5 | 6 | if(!visible) return null; 7 | return ( 8 | 9 | 10 | 11 | ); 12 | }; 13 | 14 | export default DimmerSpinner; -------------------------------------------------------------------------------- /bitimulate-frontend/src/lib/api/common.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const getCurrencyInfo = () => axios.get('/api/v1.0/common/currency-info'); 4 | export const getKrwRate = () => axios.get('/api/v1.0/common/krw-rate'); 5 | export const getNext = (url) => axios.get(url); 6 | export const getTopRanking = (type) => axios.get(`/api/v1.0/common/ranking?type=${type ? type : ''}`); 7 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/LoadMore/LoadMore.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './LoadMore.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const LoadMore = ({onClick}) => ( 8 |
9 | 더 보기 10 |
11 | ); 12 | 13 | export default LoadMore; -------------------------------------------------------------------------------- /bitimulate-frontend/.storybook/config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */ 2 | 3 | import { configure } from '@storybook/react'; 4 | 5 | const req = require.context('components', true, /.stories.js$/) 6 | 7 | function loadStories() { 8 | req.keys().forEach(filename => req(filename)) 9 | } 10 | 11 | configure(loadStories, module); 12 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/lib/api/user.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const getMetaInfo = () => axios.get('/api/v1.0/user/me/metainfo'); 4 | export const patchMetaInfo = (patchData) => axios.patch('/api/v1.0/user/me/metainfo', patchData); 5 | export const getWallet = () => axios.get('/api/v1.0/wallet'); 6 | export const getEarningsHistory = () => axios.get('/api/v1.0/user/me/earnings-history'); -------------------------------------------------------------------------------- /bitimulate-frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /bitimulate-frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /bitimulate-frontend/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Paper/Paper.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Paper.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const Paper = ({children, className}) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | export default Paper; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/InputError/InputError.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .input-error { 4 | background: material-color('red', '100'); 5 | border: 1px solid material-color('red', '200'); 6 | color: material-color('red', '700'); 7 | padding: 0.5rem; 8 | font-size: 0.80rem; 9 | margin-top: 0.5rem; 10 | margin-bottom: 0.5rem; 11 | &.no-margin-top { 12 | margin-top: 0; 13 | } 14 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/ProfitChart/ProfitChart.scss: -------------------------------------------------------------------------------- 1 | .not-available { 2 | padding-top: 3rem; 3 | padding-bottom: 3rem; 4 | font-size: 2rem; 5 | font-weight: 300; 6 | color: rgb(67, 67, 67); 7 | text-align: center; 8 | } 9 | 10 | .profit-chart { 11 | margin-top: 1.5rem; 12 | width: 100%; 13 | height: 500px; 14 | .chart { 15 | width: 100%; 16 | height: 100%; 17 | } 18 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/lib/hoc/scuize.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | export default (shouldComponentUpdate) => (FunctionalComponent) => class extends Component { 4 | shouldComponentUpdate(nextProps, nextState) { 5 | return shouldComponentUpdate.bind(this)(nextProps, nextState); 6 | } 7 | 8 | render() { 9 | return 10 | } 11 | } -------------------------------------------------------------------------------- /bitimulate-backend/src/api/v1.0/orders/index.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | 3 | const orders = new Router(); 4 | const ordersCtrl = require('./orders.ctrl'); 5 | const needAuth = require('lib/middlewares/needAuth'); 6 | 7 | orders.get('/', ordersCtrl.getOrders); 8 | orders.post('/', needAuth, ordersCtrl.createOrder); 9 | orders.post('/:id/cancel', needAuth, ordersCtrl.cancelOrder); 10 | module.exports = orders; -------------------------------------------------------------------------------- /bitimulate-frontend/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Logo/Logo.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Logo.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { Link } from 'react-router-dom'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const Logo = () => { 9 | return ( 10 | 11 | bitimulate 12 | 13 | ); 14 | }; 15 | 16 | export default Logo; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/HoverCard/HoverCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './HoverCard.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const HoverCard = ({className, ...rest}) => { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | }; 14 | 15 | export default HoverCard; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/SpinnerBlock/SpinnerBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './SpinnerBlock.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { Spinner } from 'components'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const SpinnerBlock = () => ( 9 |
10 | 11 |
12 | ); 13 | 14 | export default SpinnerBlock; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Fullscreen/Fullscreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Fullscreen.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const Fullscreen = ({className, ...rest}) => { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | }; 14 | 15 | export default Fullscreen; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SidebarWrapper/SidebarWrapper.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './SidebarWrapper.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const SidebarWrapper = ({children, visible}) => ( 8 |
9 | {children} 10 |
11 | ); 12 | 13 | export default SidebarWrapper; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SortReverser/SortReverser.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | .sort-reverser { 3 | cursor: pointer; 4 | font-size: 1.1rem; 5 | color: material-color('grey', '600'); 6 | transition: all .15s ease-in; 7 | &:hover { 8 | color: material-color('grey', '800'); 9 | } 10 | &:hover { 11 | color: material-color('grey', '900'); 12 | } 13 | &.asc { 14 | transform: rotate(180deg); 15 | } 16 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Card/Card.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Card.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const Card = ({children, className, noPadding, ...rest}) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | export default Card; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/TradeIndexOptions/index.js: -------------------------------------------------------------------------------- 1 | import TradeIndexOptions from './TradeIndexOptions.js' 2 | import { connect } from 'react-redux' 3 | 4 | const mapDispatchToProps = (dispatch, ownProps) => { 5 | return {} 6 | } 7 | 8 | const mapStateToProps = (state, ownProps) => { 9 | return {}; 10 | } 11 | 12 | export default connect( 13 | mapStateToProps, 14 | mapDispatchToProps, 15 | )(TradeIndexOptions) 16 | -------------------------------------------------------------------------------- /bitimulate-backend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "standard", 3 | "rules": { 4 | "indent": [ 5 | "warn", 6 | 2 7 | ], 8 | "semi": [ 9 | "error", 10 | "always" 11 | ], 12 | "no-unused-vars": 1, 13 | "eol-last": 0, 14 | "space-before-function-paren": 0, 15 | "no-trailing-spaces": 0, 16 | "keyword-spacing": 0 17 | } 18 | }; -------------------------------------------------------------------------------- /bitimulate-backend/workers.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [{ 3 | "name": "crawler", 4 | "script" : "./src/crawler", 5 | "env": { 6 | "NODE_PATH": "src" 7 | } 8 | }, 9 | { 10 | "name": "worker", 11 | "script" : "./src/worker", 12 | "env": { 13 | "NODE_PATH": "src" 14 | } 15 | }, 16 | { 17 | "name": "trader", 18 | "script" : "./src/trader", 19 | "env": { 20 | "NODE_PATH": "src" 21 | } 22 | }] 23 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Input/Input.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Input.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const Input = ({big, fullWidth, className, ...rest}) => { 8 | return ( 9 | 13 | ); 14 | }; 15 | 16 | export default Input; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/LabelBlock/LabelBlock.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .label-block { 4 | padding: 0.5rem; 5 | display: flex; 6 | flex-direction: column; 7 | 8 | .label { 9 | font-size: 0.75rem; 10 | font-weight: bold; 11 | color: material-color('grey', '800'); 12 | } 13 | .content { 14 | margin-top: 0.1rem; 15 | color: material-color('grey', '600'); 16 | font-size: 0.95rem; 17 | font-weight: 300; 18 | } 19 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/NavItem/NavItem.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .nav-item { 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | padding-left: 1rem; 8 | padding-right: 1rem; 9 | font-weight: 400; 10 | color: material-color('grey', '50'); 11 | opacity: 0.9; 12 | cursor: pointer; 13 | transition: all .2s; 14 | user-select: none; 15 | &:hover { 16 | font-weight: 700; 17 | opacity: 1; 18 | } 19 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/TextButton/TextButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './TextButton.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const TextButton = ({className, right, children, ...rest}) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | }; 14 | 15 | export default TextButton; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/OrderBook/OrderBook.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .order-book { 4 | margin-top: 1rem; 5 | display: flex; 6 | flex-direction: row; 7 | @include media("=medium") { 15 | margin-left: 1rem; 16 | } 17 | @include media(" { 4 | const { user } = ctx.request; 5 | 6 | if(!user) { 7 | ctx.status = 403; 8 | return; 9 | } 10 | 11 | try { 12 | const userData = await User.findById(user._id); 13 | const { wallet, walletOnOrder } = userData; 14 | ctx.body = { 15 | wallet, 16 | walletOnOrder 17 | }; 18 | } catch (e) { 19 | ctx.throw(e, 500); 20 | } 21 | }; -------------------------------------------------------------------------------- /bitimulate-backend/src/worker/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { CronJob } = require('cron'); 3 | const db = require('db'); 4 | const processWallets = require('./processWallets'); 5 | // require('mongoose').set('debug', true); 6 | 7 | function initialize() { 8 | db.connect(); 9 | // processWallets(); 10 | const jobProcessWallets = new CronJob({ 11 | cronTime: '0 * * * *', 12 | onTick: processWallets 13 | }); 14 | 15 | jobProcessWallets.start(); 16 | } 17 | 18 | initialize(); -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/AlignRight/AlignRight.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './AlignRight.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const AlignRight = ({children, className, ...rest}) => { 8 | return ( 9 |
10 |
11 | {children} 12 |
13 |
14 | ); 15 | }; 16 | 17 | export default AlignRight; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/InputError/InputError.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './InputError.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const InputError = ({children, error, noMarginTop, ...rest}) => { 8 | if(!error) return null; 9 | return ( 10 |
11 | {error} 12 |
13 | ); 14 | }; 15 | 16 | export default InputError; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/WalletHistorySubpage/WalletHistorySubpage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './WalletHistorySubpage.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { WalletHistories } from 'containers'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const WalletHistorySubpage = () => ( 9 |
10 |

거래 내역

11 | 12 |
13 | ); 14 | 15 | export default WalletHistorySubpage; -------------------------------------------------------------------------------- /bitimulate-frontend/src/lib/api/orders.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import queryString from 'query-string'; 3 | 4 | 5 | export const createOrder = ({ currencyPair, price, amount, sell }) => axios.post('/api/v1.0/orders', { 6 | currencyPair, price, amount, sell 7 | }); 8 | 9 | export const getOrders = ({currencyPair, status}) => { 10 | return axios.get(`/api/v1.0/orders?${queryString.stringify({currencyPair, status})}`); 11 | } 12 | export const cancelOrder = (id) => axios.post(`/api/v1.0/orders/${id}/cancel`); 13 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import App from 'components/App'; 3 | import { BrowserRouter, Route } from 'react-router-dom'; 4 | import { Provider } from 'react-redux'; 5 | import ReactGA from 'react-ga'; 6 | 7 | ReactGA.initialize('UA-110163170-1'); 8 | 9 | const Root = ({store}) => { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default Root; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/FlexBox/FlexBox.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './FlexBox.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const FlexBox = ({row, column, className, children, ...rest}) => { 8 | return ( 9 |
15 | { children } 16 |
17 | ); 18 | }; 19 | 20 | export default FlexBox; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/TextButton/TextButton.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .text-button { 4 | padding-bottom: 0.15rem; 5 | border-bottom: 1px solid transparent; 6 | cursor: pointer; 7 | &.right { 8 | margin-left: auto; 9 | } 10 | &:hover { 11 | color: material-color('teal', '400'); 12 | border-bottom: 1px solid material-color('teal', '600'); 13 | } 14 | &:active { 15 | color: material-color('teal', '700'); 16 | border-bottom: 1px solid material-color('teal', '700'); 17 | } 18 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/RewardWalletForm/RewardWalletForm.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | .reward-wallet-form { 3 | width: 600px; 4 | @include media(" { 8 | return ( 9 |
10 |
{label}
11 |
12 | {children} 13 |
14 |
15 | ); 16 | }; 17 | 18 | export default LabelBlock; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/TradeHistory/TradeHistory.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | .trade-history { 3 | margin-bottom: 5rem; 4 | margin-top: 1rem; 5 | display: flex; 6 | flex-direction: row; 7 | 8 | 9 | @include media("=medium") { 17 | margin-left: 1rem; 18 | } 19 | @include media(" { 8 | return ( 9 |
10 |

{title}

11 |
12 | {children} 13 |
14 |
15 | ); 16 | }; 17 | 18 | export default SectionWithTitle; -------------------------------------------------------------------------------- /bitimulate-frontend/src/containers/SocketSubscriber.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import socket from 'lib/socket'; 3 | 4 | class SocketSubscriber extends Component { 5 | componentDidMount() { 6 | const { channel } = this.props; 7 | socket.subscribe(channel); 8 | } 9 | 10 | componentWillUnmount() { 11 | const { channel } = this.props; 12 | socket.unsubscribe(channel); 13 | } 14 | 15 | 16 | render() { 17 | return ( 18 | null 19 | ); 20 | } 21 | } 22 | 23 | export default SocketSubscriber; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Block/Block.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Block.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const Block = ({color, background, children, center, shadow}) => { 8 | const style = { 9 | color, 10 | background 11 | }; 12 | 13 | return ( 14 |
18 | {children} 19 |
20 | ); 21 | }; 22 | 23 | export default Block; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SidebarWrapper/SidebarWrapper.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .sidebar-wrapper { 4 | transform: translate(0%); 5 | transition: all 0.1s ease-in-out; 6 | @include material-shadow(5,2); 7 | &.hidden { 8 | transform: translate(100%); 9 | box-shadow: none; 10 | } 11 | display: flex; 12 | flex-direction: column; 13 | position: fixed; 14 | right: 0; 15 | top: 0; 16 | background: material-color('grey', '800'); 17 | height: 100vh; 18 | width: 300px; 19 | z-index: z-index-for('sidebar'); 20 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Paper/Paper.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .paper { 4 | width: 768px; 5 | padding: 2rem; 6 | background: white; 7 | @include material-shadow(3); 8 | z-index: 4; 9 | position: absolute; 10 | top: 15%; 11 | margin-bottom: 3rem; 12 | left: 50%; 13 | transform: translate(-50%); 14 | @include media(" { 10 | const regex = /.\/(.*?).js$/; 11 | const moduleName = regex.test(key) && key.match(regex)[1]; 12 | modules[moduleName] = req(key).default; 13 | }); 14 | 15 | modules['pender'] = penderReducer; 16 | 17 | export default combineReducers(modules); -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/WalletProfitSubpage/WalletProfitSubpage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './WalletProfitSubpage.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { Profit } from 'containers'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const WalletProfitSubpage = () => ( 9 |
10 |

11 | 수익률 12 |

13 |
14 | 15 |
16 |
17 | ); 18 | 19 | export default WalletProfitSubpage; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/WalletSubpage/WalletSubpage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './WalletSubpage.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { Wallets } from 'components'; 5 | import { WalletsContainer } from 'containers'; 6 | 7 | const cx = classNames.bind(styles); 8 | 9 | const WalletSubpage = () => { 10 | return ( 11 |
12 |

13 | 내 지갑 14 |

15 | 16 |
17 | ); 18 | }; 19 | 20 | export default WalletSubpage; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/UserButton/UserButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './UserButton.scss'; 3 | import classNames from 'classnames/bind'; 4 | import UserIcon from 'react-icons/lib/io/person'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const UserButton = ({displayName, onClick}) => { 9 | return ( 10 |
11 | 12 |
13 | {displayName} 14 |
15 |
16 | ); 17 | }; 18 | 19 | export default UserButton; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/templates/RegisterTemplate/RegisterTemplate.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './RegisterTemplate.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const RegisterTemplate = ({children}) => { 8 | return ( 9 |
10 |

회원 등록

11 |

거의 다 끝났습니다. 가상화폐 모의 거래소 이용에 필요한 몇가지 정보를 입력하세요.

12 |
13 | {children} 14 |
15 |
16 | ); 17 | }; 18 | 19 | export default RegisterTemplate; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SortReverser/SortReverser.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './SortReverser.scss'; 3 | import classNames from 'classnames/bind'; 4 | import AscIcon from 'react-icons/lib/fa/sort-amount-asc'; 5 | import DescIcon from 'react-icons/lib/fa/sort-amount-desc'; 6 | 7 | const cx = classNames.bind(styles); 8 | 9 | const SortReverser = ({asc, onToggle}) => { 10 | return ( 11 |
12 | {asc ? : } 13 |
14 | ); 15 | }; 16 | 17 | export default SortReverser; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Hamburger/Hamburger.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Hamburger.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const Hamburger = ({active, onToggle}) => ( 8 |
9 |
10 | 11 | 12 | 13 |
14 |
15 | ); 16 | 17 | export default Hamburger; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Input/Input.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .input { 4 | outline: none; 5 | border-radius: 2px; 6 | border: 1px solid material-color('grey', '400'); 7 | padding: 0.5rem; 8 | font-size: 1.25rem; 9 | transition: color .3s, border-color .3s; 10 | &.full-width { 11 | width: 100%; 12 | } 13 | &:focus { 14 | border: 1px solid material-color('teal', '600'); 15 | color: material-color('teal', '500'); 16 | &::placeholder { 17 | color: material-color('teal', '400'); 18 | } 19 | } 20 | } 21 | 22 | .input + .input { 23 | margin-top: 0.5rem; 24 | } -------------------------------------------------------------------------------- /bitimulate-backend/src/ws/utils.js: -------------------------------------------------------------------------------- 1 | const LZUTF8 = require('lzutf8'); 2 | 3 | const compress = (str) => { 4 | return new Promise((resolve, reject) => { 5 | LZUTF8.compressAsync(str, { 6 | outputEncoding: 'BinaryString' 7 | }, (result, error) => { 8 | if(error) reject(error); 9 | resolve(result); 10 | }); 11 | }); 12 | } 13 | 14 | exports.compress = compress; 15 | 16 | const parseJSON = (str) => { 17 | let parsed = null; 18 | try { 19 | parsed = JSON.parse(str); 20 | } catch (e) { 21 | return null; 22 | } 23 | return parsed; 24 | }; 25 | 26 | exports.parseJSON = parseJSON; 27 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/NavItem/NavItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './NavItem.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { NavLink } from 'react-router-dom'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const NavItem = ({children, to}) => { 9 | if(to) { 10 | return ( 11 | {children} 12 | ) 13 | } 14 | return ( 15 |
16 | {children} 17 |
18 | ); 19 | }; 20 | 21 | export default NavItem; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/ScreenMask/ScreenMask.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .fade-enter { 4 | animation-duration: .15s; 5 | animation-fill-mode: both; 6 | animation-timing-function: ease-in; 7 | animation-name: fadeIn; 8 | } 9 | 10 | .fade-leave { 11 | animation-duration: .15s; 12 | animation-fill-mode: both; 13 | animation-timing-function: ease-out; 14 | animation-name: fadeOut; 15 | } 16 | 17 | 18 | .screen-mask { 19 | z-index: z-index-for('screen-mask'); 20 | position: fixed; 21 | top: 0; 22 | left: 0; 23 | right: 0; 24 | bottom: 0; 25 | background: rgba(255,255,255,0.70); 26 | } -------------------------------------------------------------------------------- /bitimulate-backend/src/api/v1.0/user/index.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | 3 | const user = new Router(); 4 | const userCtrl = require('./user.ctrl'); 5 | const needAuth = require('lib/middlewares/needAuth'); 6 | 7 | user.get('/me/metainfo', needAuth, userCtrl.getMetaInfo); 8 | user.patch('/me/metainfo', needAuth, userCtrl.patchMetaInfo); 9 | user.get('/me/earnings-history', needAuth, userCtrl.getEarningsHistory); 10 | user.get('/:displayName/orders', userCtrl.checkUserExists, userCtrl.getOrdersOfUser); 11 | user.get('/:displayName/wallet', userCtrl.checkUserExists, userCtrl.getWalletOfUser); 12 | 13 | module.exports = user; -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/common/getExchangeRate.js: -------------------------------------------------------------------------------- 1 | const cache = require('lib/cache'); 2 | const axios = require('axios'); 3 | 4 | async function getExchangeRate() { 5 | try { 6 | const cached = await cache.get('exchage-rate'); 7 | if(cached) { 8 | return cached; 9 | } 10 | const response = await axios.get('http://api.fixer.io/latest?base=USD'); 11 | const data = { 12 | KRW: response.data.rates.KRW 13 | }; 14 | // caches one hour 15 | cache.set('exchage-rate', data, 3600); 16 | return data; 17 | } catch (e) { 18 | throw(e); 19 | } 20 | } 21 | 22 | module.exports = getExchangeRate; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/templates/PageTemplate/PageTemplate.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .page { 4 | header { 5 | width: 100%; 6 | position: fixed; 7 | top: 0; 8 | left: 0; 9 | z-index: z-index-for('header'); 10 | } 11 | 12 | .content { 13 | &.padding { 14 | padding-top: 3.5rem; 15 | } 16 | &.responsive { 17 | padding-left: 1rem; 18 | padding-right: 1rem; 19 | @include responsive(); 20 | &.mobile-no-padding { 21 | @include media(" poloAxios.get(`https://poloniex.com/public?command=returnOrderBook¤cyPair=${currencyPair}&depth=20`); 8 | export const getTopOrderBook = (currencyPair) => poloAxios.get(`https://poloniex.com/public?command=returnOrderBook¤cyPair=${currencyPair}&depth=1`); 9 | export const getTradeHistory = ({currencyPair, start, end}) => poloAxios.get(`https://poloniex.com/public?command=returnTradeHistory¤cyPair=${currencyPair}${start ? `&start=${start}&end=${end}` : ''}`) -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/HorizontalLabelInput/HorizontalLabelInput.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './HorizontalLabelInput.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { Input } from 'components'; 5 | const cx = classNames.bind(styles); 6 | 7 | const HorizontalLabelInput = ({label, currency, ...rest}) => { 8 | return ( 9 |
10 | 13 | 14 | { currency &&
{currency}
} 15 |
16 | ); 17 | }; 18 | 19 | export default HorizontalLabelInput; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/HeaderNav/HeaderNav.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './HeaderNav.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { FlexBox, NavItem } from 'components'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const HeaderNav = ({logged}) => { 9 | return ( 10 | 12 | 13 | 거래소 14 | 15 | {logged && 16 | 내 지갑 17 | } 18 | 19 | 랭킹 20 | 21 | 22 | ); 23 | }; 24 | 25 | export default HeaderNav; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/templates/ReportTemplate/ReportTemplate.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './ReportTemplate.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { ResponsiveAd } from 'components'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const ReportTemplate = ({tradeHistory, wallet}) => ( 9 |
10 |
{wallet}
11 |
12 | 13 |
14 |
15 |

거래내역

16 | {tradeHistory} 17 |
18 |
19 | ); 20 | 21 | export default ReportTemplate; -------------------------------------------------------------------------------- /bitimulate-backend/src/db/index.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | require('mongoose-double')(mongoose); 3 | const log = require('lib/log'); 4 | 5 | const { 6 | MONGO_URI: mongoURI 7 | } = process.env; 8 | 9 | module.exports = (function () { 10 | mongoose.Promise = global.Promise; 11 | 12 | return { 13 | connect () { 14 | return mongoose.connect(mongoURI, { 15 | useMongoClient: true 16 | }).then( 17 | () => { 18 | log.info('Successfully connected to mongodb'); 19 | } 20 | ).catch(e => { 21 | console.error(e); 22 | }); 23 | }, 24 | disconnect () { 25 | return mongoose.disconnect(); 26 | } 27 | }; 28 | })(); -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/RegisterForm/RegisterForm.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .register-form { 4 | .description { 5 | color: #757575; 6 | white-space: pre-wrap; 7 | margin-bottom: 0.5rem; 8 | @include media(" { 5 | st[key] = (typeof object) === 'string' ? object : JSON.stringify(object); 6 | }, 7 | get: (key) => { 8 | if(!st[key]) { 9 | return null; 10 | } 11 | const value = st[key]; 12 | 13 | try { 14 | const parsed = JSON.parse(value); 15 | return parsed; 16 | } catch(e) { 17 | return value; 18 | } 19 | }, 20 | remove: (key) => { 21 | if(localStorage) { 22 | return localStorage.removeItem(key); 23 | } 24 | delete st[key]; 25 | } 26 | } 27 | })(); -------------------------------------------------------------------------------- /bitimulate-backend/src/api/v1.0/index.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | const auth = require('./auth'); 3 | const wallet = require('./wallet'); 4 | const chartData = require('./chartData'); 5 | const common = require('./common'); 6 | const exchange = require('./exchange'); 7 | const user = require('./user'); 8 | const orders = require('./orders'); 9 | 10 | const api = new Router(); 11 | 12 | api.use('/auth', auth.routes()); 13 | api.use('/wallet', wallet.routes()); 14 | api.use('/chart-data', chartData.routes()); 15 | api.use('/common', common.routes()); 16 | api.use('/exchange', exchange.routes()); 17 | api.use('/user', user.routes()); 18 | api.use('/orders', orders.routes()); 19 | 20 | module.exports = api; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/IntroQuestion/IntroQuestion.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './IntroQuestion.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const IntroQuestion = ({onClick}) => ( 8 |
9 |
10 |

11 | 도전! 가상화폐 모의거래!
12 | 300 리플 쏜다! 13 |

14 |

15 | 실제 거래소의 실시간 데이터에 기반하여 16 |
모의 거래를 해보세요! 17 |

매달 수익률 랭킹 1위에게 상금이 지급됩니다. 18 |

19 |
20 |
21 | 모의거래 시작하기 22 |
23 |
24 | ); 25 | 26 | export default IntroQuestion; -------------------------------------------------------------------------------- /bitimulate-frontend/config/polyfills.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof Promise === 'undefined') { 4 | // Rejection tracking prevents a common issue where React gets into an 5 | // inconsistent state due to an error, but it gets swallowed by a Promise, 6 | // and the user has no idea what causes React's erratic future behavior. 7 | require('promise/lib/rejection-tracking').enable(); 8 | window.Promise = require('promise/lib/es6-extensions.js'); 9 | } 10 | 11 | // fetch() polyfill for making API calls. 12 | require('whatwg-fetch'); 13 | 14 | // Object.assign() is commonly used with React. 15 | // It will use the native implementation if it's present and isn't buggy. 16 | Object.assign = require('object-assign'); 17 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/HorizontalLabelInput/HorizontalLabelInput.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .horizontal-label-input { 4 | width: 100%; 5 | display: flex; 6 | 7 | align-items: center; 8 | label { 9 | font-weight: 600; 10 | } 11 | input { 12 | width: 70%; 13 | margin-left: auto; 14 | padding-right: 3rem; 15 | } 16 | 17 | position: relative; 18 | .currency { 19 | position: absolute; 20 | right: 1rem; 21 | top: 50%; 22 | transform: translateY(-50%); 23 | font-weight: 600; 24 | font-size: 0.85rem; 25 | color: material-color('grey', '600'); 26 | } 27 | } 28 | 29 | .horizontal-label-input + .horizontal-label-input { 30 | margin-top: 1rem; 31 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/ScreenMask/ScreenMask.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './ScreenMask.scss'; 3 | import classNames from 'classnames/bind'; 4 | import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const ScreenMask = ({visible}) => { 9 | return ( 10 | 18 | { visible &&
} 19 | 20 | ); 21 | }; 22 | 23 | export default ScreenMask; -------------------------------------------------------------------------------- /bitimulate-backend/src/api/v1.0/auth/index.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router'); 2 | 3 | const auth = new Router(); 4 | const authCtrl = require('./auth.ctrl'); 5 | 6 | auth.get('/exists/email/:email', authCtrl.checkEmail); 7 | auth.get('/exists/display-name/', authCtrl.checkDisplayName); 8 | auth.get('/exists/display-name/:displayName', authCtrl.checkDisplayName); 9 | auth.post('/register/local', authCtrl.localRegister); 10 | auth.post('/register/:provider(facebook|google)', authCtrl.socialRegister); 11 | auth.post('/login/local', authCtrl.localLogin); 12 | auth.post('/login/:provider(facebook|google)', authCtrl.socialLogin); 13 | auth.get('/check', authCtrl.check); 14 | auth.post('/logout', authCtrl.logout); 15 | 16 | module.exports = auth; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Dimmer/Dimmer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import styles from './Dimmer.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | class Dimmer extends Component { 8 | componentDidMount() { 9 | // hides scroll-y 10 | document.body.style.overflowY = 'hidden'; 11 | } 12 | 13 | componentWillUnmount() { 14 | // shows scroll-y 15 | document.body.style.overflowY = 'auto'; 16 | } 17 | 18 | render() { 19 | const { ...rest } = this.props; 20 | 21 | return ( 22 |
23 | 24 |
25 | ) 26 | } 27 | 28 | } 29 | 30 | export default Dimmer; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/TradeIndexSubpage/TradeIndexSubpage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './TradeIndexSubpage.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { TradeIndexContainer, TradeIndexOptionsContainer } from 'containers'; 5 | import { Helmet } from 'react-helmet'; 6 | 7 | const cx = classNames.bind(styles); 8 | 9 | const TradeIndexSubpage = () => { 10 | return ( 11 |
12 | 13 | 거래소 :: Bitimulate 14 | 15 | 16 | 17 | 18 |
19 | ); 20 | }; 21 | 22 | export default TradeIndexSubpage; -------------------------------------------------------------------------------- /bitimulate-frontend/src/containers/ScreenMaskContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { ScreenMask } from 'components'; 4 | 5 | 6 | class ScreenMaskContainer extends Component { 7 | 8 | componentDidUpdate(prevProps, prevState) { 9 | if(prevProps.visible !== this.props.visible) { 10 | // if visible value is changed, hide or show the scrollbar 11 | document.body.style.overflowY = this.props.visible ? 'hidden' : 'auto'; 12 | } 13 | } 14 | 15 | 16 | render() { 17 | return () 18 | } 19 | } 20 | 21 | export default connect( 22 | (state) => ({ 23 | visible: state.base.getIn(['screenMask', 'visible']) 24 | }) 25 | )(ScreenMaskContainer); 26 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/BgColor/BgColor.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import styles from './BgColor.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | class BgColor extends Component { 8 | previousColor = null; 9 | 10 | setBackgroundColor = (color) => { 11 | document.body.style.background = color; 12 | } 13 | 14 | componentDidMount() { 15 | const { color } = this.props; 16 | this.previousColor = document.body.style.background; 17 | this.setBackgroundColor(color); 18 | } 19 | 20 | componentWillUnmount() { 21 | this.setBackgroundColor(this.previousColor); 22 | } 23 | 24 | render() { 25 | return
; 26 | } 27 | } 28 | 29 | export default BgColor; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Option/Option.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Option.scss'; 3 | import classNames from 'classnames/bind'; 4 | import BlankIcon from 'react-icons/lib/md/check-box-outline-blank'; 5 | import CheckBoxIcon from 'react-icons/lib/md/check-box'; 6 | 7 | const cx = classNames.bind(styles); 8 | 9 | const Option = ({children, active, onClick}) => { 10 | return ( 11 |
14 |
15 | 16 | 17 |
18 |
{children}
19 |
20 | ); 21 | }; 22 | 23 | export default Option; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Spinner/Spinner.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Spinner = ({size="200px", color="currentColor", ...rest}) => { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default Spinner; -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/log.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const Moment = require('moment'); 3 | 4 | function getTime() { 5 | const now = new Moment(); 6 | const time = chalk.dim(`[${now.format('HH:mm:ss')}]`); 7 | return time; 8 | } 9 | 10 | function log(...message) { 11 | const time = getTime(); 12 | const type = chalk.bold('[LOG]'); 13 | console.log(`${time}${type}`, ...message); 14 | } 15 | 16 | log.info = (...message) => { 17 | const time = getTime(); 18 | const type = chalk.bold(chalk.cyan('[INFO]')); 19 | console.info(`${time}${type}`, ...message); 20 | }; 21 | 22 | log.error = (...message) => { 23 | const time = getTime(); 24 | const type = chalk.bold(chalk.red('[ERROR]')); 25 | console.error(`${time}${type}`, ...message); 26 | }; 27 | 28 | module.exports = log; -------------------------------------------------------------------------------- /bitimulate-backend/src/db/models/EarningsHistory.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | require('mongoose-double')(mongoose); 3 | const User = require('./User'); 4 | 5 | const { Schema } = mongoose; 6 | // const { Types } = Schema; 7 | 8 | const EarningsHistory = new Schema({ 9 | ratio: Schema.Types.Double, 10 | userId: { 11 | type: Schema.Types.ObjectId, 12 | ref: User 13 | } 14 | }, { timestamps: true }); 15 | 16 | EarningsHistory.index({ createdAt: 1 }, {expireAfterSeconds: 60 * 60 * 24 * 30}); 17 | EarningsHistory.index({ userId: 1 }); 18 | 19 | EarningsHistory.statics.create = function(userId, ratio) { 20 | const earnings = new this({ 21 | userId, 22 | ratio 23 | }); 24 | 25 | return earnings.save(); 26 | }; 27 | 28 | module.exports = mongoose.model('EarningsHistory', EarningsHistory); -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SocialLoginButton/SocialLoginButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './SocialLoginButton.scss'; 3 | import classNames from 'classnames/bind'; 4 | import FacebookIcon from 'react-icons/lib/io/social-facebook'; 5 | import GoogleIcon from 'react-icons/lib/io/social-googleplus'; 6 | 7 | const cx = classNames.bind(styles); 8 | 9 | const SocialLoginButton = ({onSocialLogin}) => { 10 | return ( 11 |
12 |
{onSocialLogin('facebook')}}> 13 | 14 |
15 |
{onSocialLogin('google')}}> 16 | 17 |
18 |
19 | ); 20 | }; 21 | 22 | export default SocialLoginButton; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/CoinMain/CoinMain.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './CoinMain.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { CoinBlock } from 'components'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | // const coinTypes = ['BTC', 'BCH', 'ETH', 'LTC', 'XRP', 'DASH', 'XMR'] 9 | const CoinMain = ({ 10 | rate 11 | }) => { 12 | const coinBlockList = rate.map( 13 | r => () 20 | ); 21 | 22 | return ( 23 |
24 | {coinBlockList} 25 |
26 | ); 27 | } 28 | 29 | export default CoinMain; -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/token.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const { JWT_SECRET: secret } = process.env; 3 | 4 | function generateToken(payload, subject) { 5 | return new Promise( 6 | (resolve, reject) => { 7 | jwt.sign(payload, secret, { 8 | issuer: 'bitimulate.com', 9 | expiresIn: '7d', 10 | subject 11 | }, (error, token) => { 12 | if(error) reject(error); 13 | resolve(token); 14 | }); 15 | } 16 | ); 17 | } 18 | 19 | function decodeToken(token) { 20 | return new Promise( 21 | (resolve, reject) => { 22 | jwt.verify(token, secret, (error, decoded) => { 23 | if(error) reject(error); 24 | resolve(decoded); 25 | }); 26 | } 27 | ); 28 | } 29 | 30 | exports.generateToken = generateToken; 31 | exports.decodeToken = decodeToken; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/WalletMenu/WalletMenu.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './WalletMenu.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { Card, ResponsiveAd } from 'components'; 5 | import { NavLink } from 'react-router-dom'; 6 | 7 | const cx = classNames.bind(styles); 8 | 9 | 10 | const WalletMenu = () => { 11 | return ( 12 | 13 | 내 지갑 14 | 거래내역 15 | 수익률 16 |
17 | 18 |
19 |
20 | ); 21 | }; 22 | 23 | export default WalletMenu; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/UserButton/UserButton.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .user-button { 4 | user-select: none; 5 | margin-left: 1rem; 6 | display: flex; 7 | flex-direction: row; 8 | align-items: center; 9 | color: white; 10 | padding-top: 0.4rem; 11 | padding-bottom: 0.4rem; 12 | padding-left: 0.8rem; 13 | padding-right: 0.8rem; 14 | border: 2px solid rgba(255,255,255,0.3); 15 | transition: all .3s; 16 | cursor: pointer; 17 | svg { 18 | font-size: 1.25rem; 19 | margin-right: 0.25rem; 20 | } 21 | .display-name { 22 | font-weight: 700; 23 | } 24 | &:hover { 25 | background: white; 26 | color: material-color('teal', '600'); 27 | border: 2px solid white; 28 | @include material-shadow(2); 29 | } 30 | &:active { 31 | background: material-color('grey', '200'); 32 | } 33 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/Wallets/Wallets.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Wallets.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { TripleWallet, WalletTable } from 'components'; 5 | const cx = classNames.bind(styles); 6 | 7 | const Wallets = ({ 8 | sum, 9 | krwRate, 10 | btcMultiplier, 11 | walletData, 12 | hideName 13 | }) => ( 14 |
15 |
16 |

17 | 현재 총합 보유 자산 18 |

19 | 24 |
25 |
26 |

화폐별 지갑

27 | 28 |
29 |
30 | ); 31 | 32 | export default Wallets; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/templates/PageTemplate/PageTemplate.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './PageTemplate.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { SidebarContainer } from 'containers'; 5 | 6 | 7 | const cx = classNames.bind(styles); 8 | 9 | const PageTemplate = ({header, children, responsive, padding, mobileNoPadding}) => { 10 | return ( 11 |
12 |
13 | {header} 14 |
15 | { header && } 16 | 17 |
22 | {children} 23 |
24 |
25 | ); 26 | }; 27 | 28 | export default PageTemplate; -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/middlewares/jwt.js: -------------------------------------------------------------------------------- 1 | const { generateToken, decodeToken } = require('../token'); 2 | 3 | module.exports = async (ctx, next) => { 4 | const token = ctx.cookies.get('access_token'); 5 | if(!token) { 6 | // if there is no token, skip! 7 | ctx.request.user = null; 8 | return next(); 9 | } 10 | 11 | try { 12 | const decoded = await decodeToken(token); 13 | const { user } = decoded; 14 | // re-issue token when its age is over 3 days 15 | if(Date.now() / 1000 - decoded.iat > 60 * 60 * 24 * 3) { 16 | const freshToken = await generateToken({ user }, 'user'); 17 | ctx.cookies.set('access_token', freshToken, { 18 | maxAge: 1000 * 60 * 60 * 24 * 7 19 | }); 20 | } 21 | ctx.request.user = user; 22 | } catch (e) { 23 | ctx.request.user = null; 24 | } 25 | 26 | return next(); 27 | }; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/TradePage/TradePage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PageTemplate, TradeIndexSubpage, TradeDetailSubpage, ResponsiveAd } from 'components'; 3 | import { HeaderContainer } from 'containers'; 4 | import { Route } from 'react-router-dom'; 5 | import classNames from 'classnames/bind'; 6 | 7 | import styles from './TradePage.scss'; 8 | const cx = classNames.bind(styles); 9 | 10 | 11 | const TradePage = ({match}) => { 12 | return ( 13 | } padding responsive> 14 |
15 | 16 |
17 | 18 | 19 | 20 |
21 | ) 22 | }; 23 | 24 | export default TradePage; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/RewardPage/RewardPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './RewardPage.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { PageTemplate } from 'components'; 5 | import { HeaderContainer, RewardWalletFormContainer } from 'containers'; 6 | 7 | const cx = classNames.bind(styles); 8 | 9 | const RewardPage = () => ( 10 | } padding responsive> 11 |
12 |

상금 지급용 지갑 설정

13 |
14 |

매달 월 수익률 랭킹 1위에게 상금이 리플(XRP)로 지급됩니다.

15 |

상급을 지급받을 리플 지갑 정보를 입력하세요.

16 |
17 |
18 | 19 |
20 |
21 |
22 | ); 23 | 24 | export default RewardPage; -------------------------------------------------------------------------------- /bitimulate-frontend/scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | const jest = require('jest'); 19 | const argv = process.argv.slice(2); 20 | 21 | // Watch unless on CI or in coverage mode 22 | if (!process.env.CI && argv.indexOf('--coverage') < 0) { 23 | argv.push('--watch'); 24 | } 25 | 26 | 27 | jest.run(argv); 28 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/TradeHistory/TradeHistory.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './TradeHistory.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { Card, TradeHistoryTable } from 'components'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const TradeHistory = ({historyData, privateOrders, onCancelOrder, onScroll, hasNext}) => { 9 | return ( 10 |
11 |
12 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 |
21 |
22 | ); 23 | }; 24 | 25 | export default TradeHistory; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/TradeIndexOptions/TradeIndexOptions.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .options { 4 | margin-top: 1rem; 5 | padding: 1rem; 6 | background: white; 7 | display: flex; 8 | flex-direction: row; 9 | align-items: center; 10 | @include material-shadow(1, 0.5); 11 | 12 | @include media(" { 9 | return ( 10 |
11 |
12 | 13 | 17 | 18 |
19 |
20 | 21 | 25 | 26 |
27 |
28 | ); 29 | }; 30 | 31 | export default OrderBook; -------------------------------------------------------------------------------- /bitimulate-frontend/src/styles/lib/mixins/_material-shadow.scss: -------------------------------------------------------------------------------- 1 | @mixin material-shadow($z-depth: 1, $strength: 1, $color: black) { 2 | @if $z-depth == 1 { 3 | box-shadow: 0 1px 3px rgba($color, $strength * 0.14), 0 1px 2px rgba($color, $strength * 0.24); 4 | } 5 | @if $z-depth == 2 { 6 | box-shadow: 0 3px 6px rgba($color, $strength * 0.16), 0 3px 6px rgba($color, $strength * 0.23); 7 | } 8 | @if $z-depth == 3 { 9 | box-shadow: 0 10px 20px rgba($color, $strength * 0.19), 0 6px 6px rgba($color, $strength * 0.23); 10 | } 11 | @if $z-depth == 4 { 12 | box-shadow: 0 15px 30px rgba($color, $strength * 0.25), 0 10px 10px rgba($color, $strength * 0.22); 13 | } 14 | @if $z-depth == 5{ 15 | box-shadow: 0 20px 40px rgba($color, $strength * 0.30), 0 15px 12px rgba($color, $strength * 0.22); 16 | } 17 | @if ($z-depth < 1) or ($z-depth > 5) { 18 | @warn "$z-depth must be between 1 and 5"; 19 | } 20 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/containers/CoinMainContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import {bindActionCreators} from 'redux'; 4 | import { CoinMain, SpinnerBlock } from 'components'; 5 | import { coinsInHome } from 'lib/variables' 6 | 7 | class CoinMainContainer extends Component { 8 | render() { 9 | const { rate } = this.props; 10 | 11 | if(rate.size === 0) { 12 | return 13 | } 14 | 15 | 16 | const filteredRate = rate.filter( 17 | r => coinsInHome.indexOf(r.get('currencyKey')) !== -1 18 | ).sort((a,b) => coinsInHome.indexOf(a.get('currencyKey')) - coinsInHome.indexOf(b.get('currencyKey'))); 19 | 20 | return ( 21 | 22 | ) 23 | } 24 | } 25 | 26 | export default connect( 27 | (state) => ({ 28 | rate: state.trade.get('rate') 29 | }), 30 | (dispatch) => ({ 31 | }) 32 | )(CoinMainContainer); -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/TripleWallet/TripleWallet.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './TripleWallet.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { limitDigit } from 'lib/utils'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const WalletBox = ({value, currency, sign}) => ( 9 |
10 |
11 | {currency} 12 |
13 |
14 | {sign} {limitDigit(value, 10, true, currency !== 'BTC')} 15 |
16 |
17 | ) 18 | 19 | const TripleWallet = ({btc, usd, krw}) => { 20 | return ( 21 |
22 | 23 | 24 | 25 |
26 | ); 27 | }; 28 | 29 | export default TripleWallet; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/RewardWalletForm/RewardWalletForm.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './RewardWalletForm.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { Input, Card, Button } from 'components'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const RewardWalletForm = ({ 9 | address, 10 | destinationTag, 11 | onChange, 12 | onSubmit 13 | }) => ( 14 | 15 |
리플 지갑
16 |
17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 | ); 25 | 26 | export default RewardWalletForm; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/Sidebar/Sidebar.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .upper-block { 4 | height: 12rem; 5 | background: #272727; 6 | box-shadow: inset 0px -8px 8px -8px rgba(0, 0, 0, 0.4); 7 | display: flex; 8 | align-items: center; 9 | justify-content: center; 10 | flex-direction: column; 11 | padding-top: 2rem; 12 | .message { 13 | font-weight: 200; 14 | text-align: center; 15 | color: white; 16 | font-size: 1.25rem; 17 | } 18 | .sign-button { 19 | margin-top: 1rem; 20 | } 21 | } 22 | 23 | .menu { 24 | display: flex; 25 | flex-direction: column; 26 | border-bottom: 1px solid rgb(85, 85, 85); 27 | .menu-item { 28 | padding: 1rem; 29 | padding-left: 1.5rem; 30 | color: white; 31 | font-size: 1.125rem; 32 | &:active { 33 | background: rgb(40, 40, 40); 34 | } 35 | } 36 | .menu-item + .menu-item { 37 | border-top: 1px solid rgb(85, 85, 85); 38 | } 39 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/store/modules/ranking.js: -------------------------------------------------------------------------------- 1 | import { createAction, handleActions } from 'redux-actions'; 2 | 3 | import { Map, List, fromJS } from 'immutable'; 4 | import { pender } from 'redux-pender'; 5 | 6 | import * as CommonAPI from 'lib/api/common'; 7 | 8 | 9 | // action types 10 | const GET_TOP_RANKING = 'ranking/GET_TOP_RANKING'; 11 | 12 | // action creator 13 | export const getTopRanking = createAction(GET_TOP_RANKING, CommonAPI.getTopRanking); 14 | 15 | // initial state 16 | const initialState = Map({ 17 | ranking: List(), 18 | count: 0, 19 | me: 0 20 | }); 21 | 22 | // reducer 23 | export default handleActions({ 24 | ...pender({ 25 | type: GET_TOP_RANKING, 26 | onSuccess: (state, action) => { 27 | const { ranking, count, me } = action.payload.data; 28 | return state.set('ranking', fromJS(ranking)) 29 | .set('count', count) 30 | .set('me', me); 31 | } 32 | }) 33 | }, initialState); -------------------------------------------------------------------------------- /bitimulate-frontend/src/containers/Core.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | // import redux dependencies 4 | import { connect } from 'react-redux'; 5 | import {bindActionCreators} from 'redux'; 6 | import * as commonActions from 'store/modules/common'; 7 | import * as tradeActions from 'store/modules/trade'; 8 | import { MsgboxContainer } from 'containers'; 9 | 10 | class Core extends Component { 11 | componentDidMount() { 12 | const { CommonActions, TradeActions } = this.props; 13 | CommonActions.getCurrencyInfo(); 14 | TradeActions.getInitialRate(); 15 | } 16 | 17 | render() { 18 | return
19 | 20 |
21 | } 22 | } 23 | 24 | export default connect( 25 | (state) => ({ 26 | 27 | }), 28 | (dispatch) => ({ 29 | CommonActions: bindActionCreators(commonActions, dispatch), 30 | TradeActions: bindActionCreators(tradeActions, dispatch) 31 | }) 32 | )(Core); 33 | -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/social.js: -------------------------------------------------------------------------------- 1 | const FB = require('fb'); 2 | const google = require('googleapis'); 3 | 4 | const plus = google.plus('v1'); 5 | 6 | function getFacebookProfile(accessToken) { 7 | return FB.api('me', { fields: ['email'], access_token: accessToken }).then( 8 | (auth) => ({ 9 | id: auth.id, 10 | email: auth.email 11 | }) 12 | ); 13 | } 14 | 15 | function getGoogleProfile(accessToken) { 16 | return new Promise((resolve, reject) => { 17 | plus.people.get({ 18 | userId: 'me', 19 | access_token: accessToken 20 | }, (err, auth) => { 21 | if(err) reject(err); 22 | resolve({ 23 | id: auth.id, 24 | email: auth.emails[0].value 25 | }); 26 | }); 27 | }); 28 | } 29 | 30 | exports.getProfile = (provider, accessToken) => { 31 | const getters = { 32 | google: getGoogleProfile, 33 | facebook: getFacebookProfile 34 | }; 35 | 36 | return getters[provider](accessToken); 37 | }; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Modal/Modal.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .enter { 4 | animation-duration: .4s; 5 | animation-fill-mode: both; 6 | animation-timing-function: ease-in-out; 7 | animation-name: bounceUpIn; 8 | } 9 | 10 | .leave { 11 | animation-duration: .4s; 12 | animation-fill-mode: both; 13 | animation-timing-function: ease-in-out; 14 | animation-name: bounceDownOut; 15 | } 16 | 17 | .modal-wrapper { 18 | // places the modal at center 19 | position: fixed; 20 | top: 50%; 21 | left: 50%; 22 | transform: translate(-50%, -50%); 23 | z-index: z-index-for('modal'); 24 | 25 | &.uncenter { 26 | top: 1rem; 27 | transform: translate(-50%); 28 | } 29 | 30 | &.mobile-fullscreen { 31 | @include media(" { 12 | const multipliers = [1, 10, 100]; 13 | return multipliers.map( 14 | multiplier => `${info.symbol} ${(info.initialValue * multiplier).toLocaleString()}` 15 | ) 16 | })(); 17 | const optionList = options.map( 18 | (option, i) => ( 19 | 20 | ) 21 | ) 22 | return ( 23 |
24 | {optionList} 25 |
26 | ); 27 | } 28 | } 29 | 30 | 31 | export default InitialMoneyOptions; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Modal/Modal.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import styles from './Modal.scss'; 3 | import classNames from 'classnames/bind'; 4 | import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'; 5 | 6 | 7 | const cx = classNames.bind(styles); 8 | 9 | class ModalWrapper extends Component { 10 | render() { 11 | const { visible, children, mobileFullscreen } = this.props; 12 | 13 | return ( 14 |
15 | 22 | { visible &&
23 | {children} 24 |
} 25 |
26 |
27 | ) 28 | } 29 | } 30 | 31 | export default ModalWrapper; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/ButtonSelector/ButtonSelector.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | 4 | .button-selector { 5 | display: flex; 6 | flex-direction: row; 7 | 8 | .option { 9 | padding-top: 0.5rem; 10 | padding-bottom: 0.5rem; 11 | padding-left: 0.5rem; 12 | padding-right: 0.5rem; 13 | font-size: 0.75rem; 14 | background: material-color('grey', '100'); 15 | color: material-color('grey', '400'); 16 | border-radius: 4px; 17 | transition: box-shadow 0.15s; 18 | user-select: none; 19 | cursor: pointer; 20 | &:hover { 21 | background: white; 22 | @include material-shadow(1, 0.25); 23 | color: material-color('grey', '600'); 24 | } 25 | &.active { 26 | transform: scale(1.1, 1.1); 27 | @include material-shadow(2, 0.5); 28 | font-weight: bold; 29 | background: white; 30 | color: material-color('cyan', '600'); 31 | } 32 | } 33 | 34 | .option + .option { 35 | margin-left: 0.5rem; 36 | } 37 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/containers/MsgboxContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import {Msgbox} from 'components' 3 | import * as baseActions from 'store/modules/base'; 4 | import { bindActionCreators } from 'redux'; 5 | import { connect } from 'react-redux'; 6 | 7 | 8 | class MsgboxContainer extends Component { 9 | handleExit = () => { 10 | const { BaseActions } = this.props; 11 | BaseActions.hideMsgbox(); 12 | } 13 | 14 | render() { 15 | const { msgbox } = this.props; 16 | const { 17 | text, type, visible 18 | } = msgbox.toJS(); 19 | const { handleExit } = this; 20 | 21 | return ( 22 | 28 | ); 29 | } 30 | } 31 | 32 | export default connect( 33 | (state) => ({ 34 | msgbox: state.base.get('msgbox') 35 | }), 36 | (dispatch) => ({ 37 | BaseActions: bindActionCreators(baseActions, dispatch) 38 | }) 39 | )(MsgboxContainer); -------------------------------------------------------------------------------- /bitimulate-frontend/src/lib/social.js: -------------------------------------------------------------------------------- 1 | import hello from 'hellojs'; 2 | 3 | hello.init({ 4 | facebook: 1456738254380084, 5 | google: '139700894213-90pmhsv3jrjaoln83f353fmjvspdibb9.apps.googleusercontent.com' 6 | }, {redirect_uri: '/redirect.html'}); 7 | 8 | export default(function () { 9 | return { 10 | facebook: () => { 11 | return new Promise((resolve, reject) => { 12 | // hellojs 는 일반 Promise 가 아닌 Promise A+ 를 사용하므로, Promise 로 감싸줌 13 | hello.login('facebook', { scope: 'email' }).then( 14 | auth => resolve(auth.authResponse.access_token), 15 | e => reject(e) 16 | ); 17 | }) 18 | }, 19 | google: () => { 20 | return new Promise((resolve, reject) => { 21 | hello.login('google', { scope: 'email' }).then( 22 | auth => resolve(auth.authResponse.access_token), 23 | e => reject(e) 24 | ); 25 | }) 26 | } 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SelectCurrency/SelectCurrency.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './SelectCurrency.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { initialCurrencies } from 'lib/variables'; 5 | 6 | const currencies = initialCurrencies; 7 | 8 | const cx = classNames.bind(styles); 9 | 10 | const Currency = ({children, active, symbol, onClick}) => ( 11 |
12 |
{symbol}
13 |
{children}
14 |
15 | ) 16 | 17 | const SelectCurrency = ({currency, onSetCurrency}) => { 18 | const currencyList = currencies.map( 19 | c => ( 20 | onSetCurrency(c.name)} 23 | key={c.name}> 24 | {c.name} 25 | 26 | ) 27 | ); 28 | 29 | return ( 30 |
31 | {currencyList} 32 |
33 | ); 34 | }; 35 | 36 | export default SelectCurrency; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SelectCurrency/SelectCurrency.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .select-currency { 4 | display: flex; 5 | .currency { 6 | border: 1px solid transparent; 7 | display: flex; 8 | align-items: center; 9 | padding: 1rem; 10 | width: 8rem; 11 | padding-top: 0.5rem; 12 | padding-bottom: 0.5rem; 13 | border-radius: 2px; 14 | transition: all .3s; 15 | background: material-color('grey', '200'); 16 | border-radius: 2px; 17 | cursor: pointer; 18 | font-weight: 700; 19 | &:hover { 20 | @include material-shadow(2); 21 | background: material-color('grey', '100'); 22 | } 23 | &.active, &:active { 24 | border: 1px solid material-color('teal', '500'); 25 | background: white; 26 | color: material-color('teal', '500'); 27 | } 28 | .symbol { 29 | font-size: 1.25rem; 30 | } 31 | .text { 32 | text-align: center; 33 | flex: 1; 34 | font-size: 0.9rem; 35 | } 36 | } 37 | 38 | .currency + .currency { 39 | margin-left: 1rem; 40 | } 41 | } -------------------------------------------------------------------------------- /bitimulate-backend/src/crawler/worker.js: -------------------------------------------------------------------------------- 1 | function Worker(works = [], notify) { 2 | this.index = 0; 3 | this.works = works; 4 | this.notify = notify; 5 | } 6 | 7 | Worker.prototype.reset = function() { 8 | this.index = 0; 9 | }; 10 | 11 | Worker.prototype.work = function() { 12 | const { works, notify } = this; 13 | if(works.length === 0) return Promise.resolve(); 14 | 15 | const promise = new Promise((resolve, reject) => { 16 | const repeat = async () => { 17 | try { 18 | await works[this.index++](); 19 | notify(); 20 | } catch (e) { 21 | // console.log(e); 22 | if(e.code !== 'ECONNABORTED') { 23 | console.error(e); 24 | } else { 25 | console.log('timed out'); 26 | } 27 | this.index--; 28 | setTimeout(repeat, 1000); 29 | return; 30 | } 31 | 32 | if(this.index >= works.length) { 33 | return resolve(); 34 | } 35 | setTimeout(repeat, 1000); 36 | }; 37 | 38 | repeat(); 39 | }); 40 | return promise; 41 | }; 42 | 43 | module.exports = Worker; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Button/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Button.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const Button = ({ 8 | children, 9 | flex, 10 | className, 11 | roundCorner, 12 | invert, 13 | flat, 14 | color, 15 | padding="0.5rem", 16 | xPadding, 17 | style, 18 | disabled, 19 | dark, 20 | onClick, 21 | theme, 22 | ...rest 23 | }) => { 24 | const dynamicStyle = { 25 | ...(xPadding ? { 26 | paddingLeft: xPadding, 27 | paddingRight: xPadding 28 | } : {}) 29 | } 30 | return ( 31 |
47 | {children} 48 |
49 | ); 50 | }; 51 | 52 | export default Button; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/OrdersTable/OrdersTable.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | @keyframes flicker { 4 | 50% { 5 | background: rgba(0, 208, 255, 0.15); 6 | } 7 | } 8 | 9 | .orders-table { 10 | .flicker { 11 | animation-duration: .5s; 12 | animation-fill-mode: both; 13 | animation-name: flicker; 14 | } 15 | .title { 16 | font-weight: 600; 17 | color: material-color('grey', '800'); 18 | } 19 | .table-head { 20 | margin-bottom: 0.5rem; 21 | margin-top: 1rem; 22 | display: flex; 23 | .col-desc { 24 | color: material-color('grey', '500'); 25 | flex: 1; 26 | font-size: 0.85rem; 27 | } 28 | } 29 | .row { 30 | display: flex; 31 | padding-top: 0.25rem; 32 | min-height: 1.4375rem; 33 | padding-bottom: 0.25rem; 34 | .value { 35 | font-size: 0.85rem; 36 | flex: 1; 37 | } 38 | margin-left: -1rem; 39 | margin-right: -1rem; 40 | padding-left: 1rem; 41 | padding-right: 1rem; 42 | &:nth-child(even) { 43 | background: material-color('grey', '100'); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/ReportPage/ReportPage.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .block { 4 | background: linear-gradient(to right, #276caf, #30a1c1); 5 | height: 25rem; 6 | z-index: 1; 7 | @include media(" fn => fn; 10 | 11 | const configureStore = (initialState) => { 12 | const enhancers = [ 13 | applyMiddleware( 14 | penderMiddleware() 15 | ), 16 | devtools({ 17 | actionsBlacklist: ['trade/UPDATE_TICKER'], 18 | maxAge: 1000 19 | }) 20 | ]; 21 | 22 | const store = createStore(modules, initialState, compose(...enhancers)); 23 | 24 | if(module.hot) { 25 | // module.hot.accept('./modules', () => { 26 | // const nextReducer = require('./modules').default; 27 | // store.replaceReducer(nextReducer); 28 | // }); 29 | } 30 | 31 | if(module.hot) { 32 | module.hot.accept('./modules', () => store.replaceReducer(modules)); 33 | } 34 | 35 | return store; 36 | }; 37 | 38 | export default configureStore; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/TripleWallet/TripleWallet.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .triple-wallet { 4 | margin-top: 1rem; 5 | display: flex; 6 | flex-direction: row; 7 | @include media(" { 12 | const { displayName } = match.params; 13 | 14 | return ( 15 | } padding> 16 | 17 | {`${displayName}님의 리포트 :: Bitimulate`} 18 | 19 | 20 |
21 |
22 | 23 |

{displayName}님의 리포트

24 |
25 | 26 |
27 | 28 |
29 | ); 30 | } 31 | 32 | export default ReportPage; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/Header/Header.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .header { 4 | height: 3.5rem; 5 | &.solid { 6 | background: linear-gradient(to right, #276caf, #30a1c1); 7 | } 8 | transition: all 0.3s ease-in; 9 | &.shadow { 10 | background: linear-gradient(to right, #276caf, #30a1c1); 11 | @include material-shadow(2, 0.75); 12 | } 13 | // background: material-color('teal', '600'); 14 | .responsive { 15 | position: relative; 16 | padding-left: 1rem; 17 | padding-right: 1rem; 18 | @include responsive(); 19 | display: flex; 20 | height: 100%; 21 | align-items: center; 22 | 23 | .right-side { 24 | .desktop-only { 25 | display: flex; 26 | @include media("=medium") { 32 | display: none; 33 | } 34 | } 35 | margin-left: auto; 36 | } 37 | } 38 | 39 | .login-button { 40 | margin-left: 1rem; 41 | &:hover { 42 | color: material-color('teal', '500'); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/lib/variables.js: -------------------------------------------------------------------------------- 1 | export const optionsPerCurrency = { 2 | // 'KRW': { 3 | // symbol: '₩', 4 | // initialValue: 1000000 5 | // }, 6 | 'USD': { 7 | symbol: '$', 8 | initialValue: 1000 9 | }, 10 | 'BTC': { 11 | symbol: 'Ƀ', 12 | initialValue: 1 13 | } 14 | } 15 | 16 | export const initialCurrencies = [ 17 | // { 18 | // name: 'KRW', 19 | // symbol: '₩' 20 | // }, 21 | { 22 | name: 'USD', 23 | symbol: '$' 24 | }, 25 | { 26 | name: 'BTC', 27 | symbol: 'Ƀ' 28 | } 29 | ]; 30 | 31 | export const chartTypes = [ 32 | { 33 | name: 'day', 34 | text: '하루', 35 | unit: '5분' 36 | }, 37 | { 38 | name: 'week', 39 | text: '일주일', 40 | unit: '30분' 41 | }, 42 | { 43 | name: 'month', 44 | text: '한달', 45 | unit: '2시간' 46 | }, 47 | { 48 | name: 'year', 49 | text: '1년', 50 | unit: '하루' 51 | }, 52 | { 53 | name: 'all', 54 | text: '전체', 55 | unit: '하루' 56 | } 57 | ]; 58 | 59 | export const coinsInHome = [ 60 | 'BTC', 61 | 'BCH', 62 | 'ETH', 63 | 'ETC', 64 | 'LTC', 65 | 'XRP', 66 | 'DASH', 67 | 'XMR' 68 | ]; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/TradeChart/TradeChart.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .trade-chart-wrapper { 4 | margin-top: 2rem; 5 | } 6 | 7 | .currency-head { 8 | padding-top: 0.25rem; 9 | padding-bottom: 0.45rem; 10 | padding-left: 0.65rem; 11 | border-left: 8px solid material-color('cyan', '600'); 12 | margin-bottom: 1rem; 13 | .title { 14 | font-size: 2rem; 15 | font-weight: 300; 16 | color: material-color('grey', '900'); 17 | } 18 | .desc { 19 | margin-top: 0.25rem; 20 | color: material-color('grey', '700'); 21 | font-size: 0.85rem; 22 | } 23 | } 24 | 25 | .trade-chart { 26 | margin-top: 0.5rem; 27 | background: material-color('grey', '200'); 28 | @include material-shadow(1,0.8); 29 | height: 450px; 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | position: relative; 34 | overflow: hidden; 35 | .unit { 36 | position: absolute; 37 | right: 0.5rem; 38 | top: 0.5rem; 39 | z-index: 2; 40 | font-size: 0.75rem; 41 | color: material-color('grey', '600'); 42 | } 43 | .chart { 44 | width: 100%; 45 | height: 100%; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/Msgbox/Msgbox.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import styles from './Msgbox.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { Modal } from 'components'; 5 | import CheckIcon from 'react-icons/lib/md/check'; 6 | import ErrorIcon from 'react-icons/lib/md/error-outline'; 7 | const cx = classNames.bind(styles); 8 | 9 | const icons = { 10 | success: CheckIcon, 11 | error: ErrorIcon, 12 | } 13 | 14 | class Msgbox extends Component { 15 | render() { 16 | const { visible, type = 'success', text, onExit } = this.props; 17 | 18 | const Icon = icons[type]; 19 | 20 | return ( 21 | 22 |
23 |
24 | { Icon && } 25 |
26 |
27 | {text} 28 |
29 |
30 |
확인
31 |
32 |
33 |
34 | ) 35 | } 36 | } 37 | 38 | export default Msgbox; -------------------------------------------------------------------------------- /bitimulate-frontend/src/store/modules/common.js: -------------------------------------------------------------------------------- 1 | import { createAction, handleActions } from 'redux-actions'; 2 | 3 | import { Map, List, fromJS } from 'immutable'; 4 | import { pender } from 'redux-pender'; 5 | import * as CommonAPI from 'lib/api/common'; 6 | 7 | // action types 8 | const GET_CURRENCY_INFO = 'common/CURRENCY_INFO'; 9 | const GET_KRW_RATE = 'common/GET_KRW_RATE'; 10 | 11 | // action creator 12 | export const getCurrencyInfo = createAction(GET_CURRENCY_INFO, CommonAPI.getCurrencyInfo); 13 | export const getKrwRate = createAction(GET_KRW_RATE, CommonAPI.getKrwRate); 14 | 15 | // initial state 16 | const initialState = Map({ 17 | currencyInfo: List(), 18 | krwRate: null 19 | }); 20 | 21 | // reducer 22 | export default handleActions({ 23 | ...pender({ 24 | type: GET_CURRENCY_INFO, 25 | onSuccess: (state, action) => { 26 | const { data: currencyInfo } = action.payload; 27 | return state.set('currencyInfo', fromJS(currencyInfo)); 28 | } 29 | }), 30 | ...pender({ 31 | type: GET_KRW_RATE, 32 | onSuccess: (state, action) => { 33 | return state.set('krwRate', action.payload.data.KRW); 34 | } 35 | }) 36 | }, initialState); -------------------------------------------------------------------------------- /bitimulate-backend/src/db/models/Order.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | require('mongoose-double')(mongoose); 3 | 4 | const { Schema } = mongoose; 5 | const { Types } = Schema; 6 | 7 | const Order = new Schema({ 8 | userId: Types.ObjectId, 9 | currencyPair: String, 10 | price: Schema.Types.Double, 11 | amount: Schema.Types.Double, 12 | processedAmount: { 13 | type: Schema.Types.Double, 14 | default: 0 15 | }, 16 | sell: Boolean, 17 | status: { 18 | type: String, 19 | enum: ['waiting', 'partial', 'processed', 'cancelled'], 20 | default: 'waiting' 21 | }, 22 | date: { 23 | type: Date, 24 | default: new Date() 25 | }, 26 | processedDate: { 27 | type: Date, 28 | default: null 29 | } 30 | }); 31 | 32 | Order.statics.findOrders = function(userId, cursor, currencyPair, status) { 33 | return this.find({ 34 | userId, 35 | ...(cursor ? { _id: { $lt: cursor } } : {}), 36 | ...(currencyPair ? { currencyPair } : {}), 37 | ...(status ? { status } : {}) 38 | }, { 39 | userId: false 40 | }).sort({ 41 | _id: -1 42 | }).limit(20).exec(); 43 | }; 44 | 45 | module.exports = mongoose.model('Order', Order); -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/CoinBlock/CoinBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './CoinBlock.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { CoinIcon, Card } from 'components'; 5 | import { limitDigit, decimalToPercentString } from 'lib/utils'; 6 | import { Link } from 'react-router-dom'; 7 | 8 | const cx = classNames.bind(styles); 9 | 10 | const CoinBlock = ({currencyKey, name , last, percent}) => ( 11 | 12 | 13 |
14 | 15 |
16 |
17 | {name} 18 |
19 |
20 |
21 | 현재 시세 22 | ({decimalToPercentString(percent)}%) 23 |
24 |
{limitDigit(last)} {currencyKey === 'BTC' ? 'USD' : 'BTC'}
25 |
26 |
27 | 28 | ); 29 | 30 | export default CoinBlock; -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/cache.js: -------------------------------------------------------------------------------- 1 | const redis = require('redis'); 2 | const bluebird = require('bluebird'); 3 | 4 | bluebird.promisifyAll(redis.RedisClient.prototype); 5 | bluebird.promisifyAll(redis.Multi.prototype); 6 | 7 | module.exports = (function() { 8 | const client = redis.createClient(); 9 | 10 | return { 11 | get client() { 12 | return client; 13 | }, 14 | set(key, value, exp) { 15 | if(exp) { 16 | return client.setAsync(key, JSON.stringify(value), 'EX', exp); 17 | } 18 | return client.setAsync(key, JSON.stringify(value)); 19 | }, 20 | get(key) { 21 | return client.getAsync(key).then(data => { 22 | if(!data) return null; 23 | return JSON.parse(data); 24 | }); 25 | }, 26 | cachify(fn, exp, prefix = '') { 27 | return async (...params) => { 28 | const key = `${prefix}${fn.name}:${JSON.stringify(params)}`; 29 | const cached = await this.get(key); 30 | if (cached) { 31 | return cached; 32 | } 33 | 34 | const result = await fn(...params); 35 | this.set(key, result, exp); 36 | return result; 37 | }; 38 | } 39 | }; 40 | })(); -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/ButtonSelector/ButtonSelector.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './ButtonSelector.scss'; 3 | import classNames from 'classnames/bind'; 4 | 5 | const cx = classNames.bind(styles); 6 | 7 | const Option = ({name, children, active, onClick}) => { 8 | return ( 9 |
10 | {children} 11 |
12 | ) 13 | } 14 | 15 | const ButtonSelector = ({ 16 | options, 17 | onSelect, 18 | value, 19 | className, 20 | ...rest 21 | }) => { 22 | const optionList = options.map( 23 | ({name, text}) => ( 24 | 30 | ) 31 | ); 32 | 33 | return ( 34 |
35 | {optionList} 36 |
37 | ); 38 | }; 39 | 40 | ButtonSelector.defaultProps = { 41 | options: [ 42 | { 43 | name: 'value', 44 | text: '텍스트' 45 | }, 46 | { 47 | name: 'value2', 48 | text: '텍스트2' 49 | } 50 | ] 51 | } 52 | 53 | export default ButtonSelector; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/PolyBackground/PolyBackground.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .poly-background { 4 | background: linear-gradient(to right bottom,#276caf,#30a1c1,#9dd5ba); 5 | width: 100%; 6 | height: 100vh; 7 | overflow: hidden; 8 | clip-path: polygon(0 0, 100% 0, 100% calc(100% - 128px), 0 100%); 9 | position: relative; 10 | transition: all .7s ease-in-out; 11 | @include media(" { 6 | // ctx.set('Last-Modified', 'Sun, 03 Sep 2017 16:04:24 GMT'); 7 | // ctx.set('Cache-Control', 'public, max-age=31536000'); 8 | ctx.body = currencyInfo; 9 | }; 10 | 11 | exports.getKrwRate = async (ctx) => { 12 | try { 13 | const cached = await getExchangeRate(); 14 | ctx.body = cached; 15 | } catch (e) { 16 | ctx.throw(e, 500); 17 | } 18 | }; 19 | 20 | exports.getRanking = async (ctx) => { 21 | const { type } = ctx.query; 22 | const { user } = ctx.request; 23 | 24 | let myRank = null; 25 | const monthly = type === 'monthly' || !type; 26 | 27 | try { 28 | const count = await User.count().exec(); 29 | const ranking = await User.getTopRanking(monthly); 30 | if(user) { 31 | const u = await User.findById(user._id).exec(); 32 | myRank = await u.getRank(monthly); 33 | } 34 | ctx.body = { 35 | count, ranking, me: user && (myRank + 1) 36 | }; 37 | } catch (e) { 38 | ctx.throw(e, 500); 39 | } 40 | }; -------------------------------------------------------------------------------- /bitimulate-frontend/src/lib/api/auth.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const checkEmail = (email) => axios.get('/api/v1.0/auth/exists/email/' + email); 4 | export const checkDisplayName = (displayName) => axios.get('/api/v1.0/auth/exists/display-name/' + displayName); 5 | export const localRegister = ({ 6 | displayName, 7 | email, 8 | password, 9 | initialMoney: { currency, index } 10 | }) => axios.post('/api/v1.0/auth/register/local', { 11 | displayName, 12 | email, 13 | password, 14 | initialMoney: { currency, index } 15 | }) 16 | export const localLogin = ({email, password}) => axios.post('/api/v1.0/auth/login/local', { 17 | email, password 18 | }); 19 | export const socialLogin = ({provider, accessToken}) => axios.post('/api/v1.0/auth/login/' + provider, { 20 | accessToken 21 | }); 22 | export const socialRegister = ({ 23 | displayName, 24 | provider, 25 | accessToken, 26 | initialMoney: { currency, index } 27 | }) => axios.post('/api/v1.0/auth/register/' + provider, { 28 | displayName, 29 | accessToken, 30 | initialMoney: { currency, index } 31 | }); 32 | export const checkLoginStatus = () => axios.get('/api/v1.0/auth/check'); 33 | export const logout = () => axios.post('/api/v1.0/auth/logout'); -------------------------------------------------------------------------------- /bitimulate-frontend/src/containers/IntroQuestionContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import {bindActionCreators} from 'redux'; 4 | import * as baseActions from 'store/modules/base'; 5 | import * as authActions from 'store/modules/auth'; 6 | import { IntroQuestion } from 'components'; 7 | import { withRouter } from 'react-router-dom'; 8 | 9 | 10 | class IntroQuestionContainer extends Component { 11 | handleClick = () => { 12 | const { BaseActions, AuthActions, history, user } = this.props; 13 | 14 | if (user) { 15 | history.push('/trade'); 16 | return; 17 | } 18 | 19 | AuthActions.toggleLoginModal(); 20 | BaseActions.setScreenMaskVisibility(true); 21 | AuthActions.setModalMode('register'); 22 | } 23 | render() { 24 | const { handleClick } = this; 25 | return ( 26 | 27 | ) 28 | } 29 | } 30 | 31 | export default connect( 32 | (state) => ({ 33 | user: state.user.get('user') 34 | }), 35 | (dispatch) => ({ 36 | BaseActions: bindActionCreators(baseActions, dispatch), 37 | AuthActions: bindActionCreators(authActions, dispatch) 38 | }) 39 | )(withRouter(IntroQuestionContainer)); -------------------------------------------------------------------------------- /bitimulate-frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import Root from './Root'; 4 | import 'styles/main.scss'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | import store from 'store'; 7 | import { AppContainer as HotContainer } from 'react-hot-loader'; 8 | import social from 'lib/social'; 9 | import socket from 'lib/socket'; 10 | import axios from 'axios'; 11 | 12 | window.axios = axios; 13 | const socketURI = process.env.NODE_ENV === 'production' 14 | ? 'wss://api.bitimulate.com/ws' 15 | : 'ws://localhost:4000/ws' 16 | 17 | if(process.env.NODE_ENV === 'production') { 18 | axios.defaults.withCredentials = true; 19 | axios.defaults.baseURL = 'https://api.bitimulate.com'; 20 | } 21 | 22 | console.log(socketURI); 23 | socket.initialize(store, socketURI); 24 | 25 | window.socket = socket; 26 | 27 | const render = (Component) => ReactDOM.render( 28 | ( 29 | 30 | 31 | 32 | ), 33 | document.getElementById('root') 34 | ); 35 | 36 | render(Root); 37 | 38 | if(module.hot) { 39 | module.hot.accept('./Root', () => render(Root)) 40 | } 41 | 42 | registerServiceWorker(); 43 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Option/Option.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | @keyframes pop { 4 | 0% { 5 | transform: scale(1, 1); 6 | } 7 | 50% { 8 | transform: scale(1.2, 1.2); 9 | } 10 | 100% { 11 | transform: scale(1, 1); 12 | } 13 | } 14 | 15 | 16 | .option { 17 | cursor: pointer; 18 | display: flex; 19 | align-items: center; 20 | user-select: none; 21 | 22 | .check-box { 23 | margin-right: 0.5rem; 24 | font-size: 1.25rem; 25 | .checked { 26 | display: none; 27 | } 28 | } 29 | 30 | .text { 31 | transform: translateY(1px); 32 | } 33 | 34 | transition: all .15s; 35 | 36 | &:hover { 37 | color: material-color('teal', '500'); 38 | } 39 | &:active, &.active { 40 | .check-box { 41 | .blank { 42 | display: none; 43 | } 44 | .checked { 45 | display: initial; 46 | } 47 | } 48 | } 49 | 50 | &.active { 51 | color: material-color('teal', '500'); 52 | font-weight: 700; 53 | 54 | .check-box { 55 | animation-duration: .2s; 56 | animation-fill-mode: both; 57 | animation-timing-function: ease-in-out; 58 | animation-name: pop; 59 | } 60 | } 61 | } 62 | 63 | .option + .option { 64 | margin-top: 1rem; 65 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/RegisterPage/RegisterPage.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | PageTemplate, 4 | RegisterTemplate, 5 | PolyBackground, 6 | Paper 7 | } from 'components'; 8 | import {HeaderContainer, RegisterFormContainer} from 'containers'; 9 | import styles from './RegisterPage.scss'; 10 | import classNames from 'classnames/bind'; 11 | import { Helmet } from 'react-helmet'; 12 | 13 | const cx = classNames.bind(styles); 14 | 15 | class RegisterPage extends Component { 16 | state = { 17 | half: false 18 | } 19 | 20 | componentDidMount() { 21 | setTimeout(() => { 22 | this.setState({ 23 | half: true 24 | }); 25 | }) 26 | } 27 | 28 | render() { 29 | const { half } = this.state; 30 | 31 | return ( 32 | }> 34 | 35 | 36 | 회원가입 :: Bitimulate 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ); 46 | } 47 | } 48 | 49 | export default RegisterPage; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/WalletMenu/WalletMenu.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | .wallet-menu { 3 | position: relative; 4 | a { 5 | display: block; 6 | padding: 1rem; 7 | font-weight: 200; 8 | border-right: 8px solid transparent; 9 | transition: .15s ease-in; 10 | &.active { 11 | font-weight: 400; 12 | color: material-color('cyan', '600'); 13 | border-right: 8px solid material-color('cyan', '600'); 14 | } 15 | } 16 | 17 | a + a { 18 | border-top: 1px solid material-color('grey', '300'); 19 | } 20 | 21 | @include media(" Object.assign({}, data, { 29 | date: data.date * 1000, 30 | name, 31 | period 32 | })); 33 | return this.create(converted); 34 | }; 35 | 36 | ChartData.statics.findByNameAndPeriod = function(name, period) { 37 | const weekly = { 38 | date: { 39 | '$lt': new Date() - 1000 * 60 * 60 * 24 * 7 40 | } 41 | }; 42 | 43 | const query = Object.assign({ 44 | name, period 45 | }, period === 300 ? weekly : { }); 46 | 47 | return this.find(query).sort({ 48 | date: 1 49 | }); 50 | }; 51 | 52 | module.exports = mongoose.model('ChartData', ChartData); -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/PolyBackground/PolyBackground.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import styles from './PolyBackground.scss'; 3 | import classNames from 'classnames/bind'; 4 | import background from 'static/images/background.png'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | class PolyBackground extends Component { 9 | 10 | state = { 11 | loaded: false 12 | } 13 | 14 | componentWillMount() { 15 | const image = new Image(); 16 | image.src = background; 17 | 18 | const cached = image.complete || (image.width+image.height) > 0; 19 | if(cached) { 20 | this.setState({ 21 | loaded: true 22 | }) 23 | return; 24 | } 25 | 26 | image.onload = () => { 27 | this.setState({ 28 | loaded: true 29 | }) 30 | }; 31 | } 32 | componentDidMount() { 33 | 34 | 35 | } 36 | 37 | render() { 38 | const { loaded } = this.state; 39 | const { children, fixed, half, home } = this.props; 40 | 41 | return ( 42 |
45 |
46 |
47 |
48 | {children} 49 |
50 |
51 | ); 52 | } 53 | } 54 | 55 | export default PolyBackground; -------------------------------------------------------------------------------- /bitimulate-frontend/src/containers/RankingContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import {bindActionCreators} from 'redux'; 4 | import { Ranking, SpinnerBlock } from 'components'; 5 | import * as rankingActions from 'store/modules/ranking'; 6 | 7 | 8 | class RankingContainer extends Component { 9 | 10 | componentDidMount() { 11 | const { RankingActions, type } = this.props; 12 | RankingActions.getTopRanking(type); 13 | } 14 | 15 | componentWillReceiveProps(nextProps) { 16 | if(nextProps.type !== this.props.type) { 17 | const { RankingActions } = this.props; 18 | RankingActions.getTopRanking(nextProps.type); 19 | } 20 | } 21 | 22 | 23 | render() { 24 | const { ranking, count, loading, type, me } = this.props; 25 | 26 | if(ranking.isEmpty() || loading) return ; 27 | 28 | return ( 29 | 30 | ) 31 | } 32 | } 33 | 34 | export default connect( 35 | (state) => ({ 36 | me: state.ranking.get('me'), 37 | count: state.ranking.get('count'), 38 | ranking: state.ranking.get('ranking'), 39 | loading: state.pender.pending['ranking/GET_TOP_RANKING'] 40 | }), 41 | (dispatch) => ({ 42 | RankingActions: bindActionCreators(rankingActions, dispatch) 43 | }) 44 | )(RankingContainer); -------------------------------------------------------------------------------- /bitimulate-backend/src/db/models/ExchangeRate.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | require('mongoose-double')(mongoose); 3 | 4 | const { Schema } = mongoose; 5 | const { Types } = Schema; 6 | 7 | const ExchangeRate = new Schema({ 8 | name: String, 9 | last: Types.Double, 10 | lowestAsk: Types.Double, 11 | highestBid: Types.Double, 12 | percentChange: Types.Double, 13 | baseVolume: Types.Double, 14 | quoteVolume: Types.Double, 15 | isFrozen: Types.Double, 16 | high24hr: Types.Double, 17 | low24hr: Types.Double, 18 | lastUpdated: { 19 | type: Date, 20 | default: new Date() 21 | } 22 | }); 23 | 24 | ExchangeRate.index({name: 1}, {name: 'rateTypeIdentifier', unique: true}); 25 | 26 | // only for temporary use 27 | ExchangeRate.statics.drop = function () { 28 | return this.remove({}).exec(); 29 | }; 30 | 31 | ExchangeRate.statics.updateTicker = function(name, data) { 32 | return this.findOneAndUpdate({name}, { 33 | ...data, 34 | lastUpdated: new Date() 35 | }, { upsert: false, new: true }).exec(); 36 | }; 37 | 38 | ExchangeRate.statics.showAll = function() { 39 | return this.find({}); 40 | }; 41 | 42 | ExchangeRate.statics.getUSDRate = function() { 43 | return this.findOne({name: 'USDT_BTC'}).exec().then( 44 | (rate) => { 45 | return 1 / rate.last.value; 46 | } 47 | ); 48 | }; 49 | 50 | module.exports = mongoose.model('ExchangeRate', ExchangeRate); -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/WalletPage/WalletPage.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .wallet-page { 4 | padding-top: 3rem; 5 | display: flex; 6 | flex-direction: row; 7 | 8 | .ad-area-mobile { 9 | width: 300px; 10 | } 11 | @include media(" { 2 | const aggregated = []; 3 | const walletData = wallet.toJS(); 4 | for(let currency in walletData) { 5 | aggregated.push({ 6 | valueOnOrder: walletOnOrder.get(currency), 7 | currency, 8 | value: wallet.get(currency) + (walletOnOrder.get(currency) || 0) 9 | }) 10 | } 11 | return aggregated; 12 | } 13 | 14 | export const getCorrespondingRate = (aggregated, rate) => { 15 | 16 | 17 | aggregated.forEach( 18 | w => { 19 | if(w.currency === 'BTC') { 20 | const btcRate = rate.find(r => r.get('currencyKey') === 'BTC'); 21 | w.currencyName = 'Bitcoin'; 22 | w.last = 1; 23 | w.percentChange = btcRate && btcRate.get('percentChange'); 24 | return; 25 | } 26 | if(w.currency === 'USD') { 27 | 28 | const btcRate = rate.find(r => r.get('currencyKey') === 'BTC'); 29 | if(!btcRate) return w; 30 | w.currencyName = 'Dollar'; 31 | w.last = 1 / btcRate.get('last'); 32 | return; 33 | } 34 | 35 | const info = rate.find(r => r.get('currencyKey') === w.currency); 36 | if(!info) return w; 37 | w.last = info.get('last'); 38 | w.currencyName = info.get('currencyName'); 39 | w.percentChange = info.get('percentChange'); 40 | } 41 | ); 42 | return aggregated; 43 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/SocialLoginButton/SocialLoginButton.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .social-login-button { 4 | display: flex; 5 | flex-direction: row; 6 | height: 2.4rem; 7 | & > div { 8 | transition: all .1s ease-in; 9 | cursor: pointer; 10 | border-radius: 2px; 11 | flex: 1; 12 | color: white; 13 | display: flex; 14 | align-items: center; 15 | justify-content: center; 16 | font-size: 1.25rem; 17 | &.facebook { 18 | border: 1px solid material-color('blue', '600'); 19 | color: material-color('blue', '600'); 20 | &:hover { 21 | color: white; 22 | background: material-color('blue', '500'); 23 | border: 1px solid material-color('blue', '500'); 24 | } 25 | &:active { 26 | border: 1px solid material-color('blue', '700'); 27 | background: material-color('blue', '700'); 28 | } 29 | } 30 | &.google { 31 | border: 1px solid material-color('red', '600'); 32 | color: material-color('red', '600'); 33 | &:hover { 34 | color: white; 35 | background: material-color('red', '500'); 36 | border: 1px solid material-color('red', '500'); 37 | } 38 | &:active { 39 | border: 1px solid material-color('red', '700'); 40 | background: material-color('red', '700'); 41 | } 42 | } 43 | } 44 | 45 | div + div { 46 | margin-left: 0.5rem; 47 | } 48 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/UserMenu/UserMenu.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | @keyframes enter { 4 | 0% { 5 | opacity: 0; 6 | transform: translateY(-16px); 7 | } 8 | 100% { 9 | opacity: 1; 10 | transform: translateY(0px); 11 | } 12 | } 13 | 14 | @keyframes leave { 15 | 0% { 16 | opacity: 1; 17 | transform: translateY(0px); 18 | } 19 | 100% { 20 | opacity: 0; 21 | transform: translateY(-16px); 22 | } 23 | } 24 | 25 | .enter { 26 | animation-duration: .2s; 27 | animation-fill-mode: both; 28 | animation-timing-function: ease-in-out; 29 | animation-name: enter; 30 | } 31 | 32 | .leave { 33 | animation-duration: .2s; 34 | animation-fill-mode: both; 35 | animation-timing-function: ease-in-out; 36 | animation-name: leave; 37 | } 38 | .user-menu { 39 | position: absolute; 40 | right: 1rem; 41 | top: 4rem; 42 | z-index: z-index-for('screen-mask'); 43 | .card { 44 | @include material-shadow(2, 0.5); 45 | .menu-item { 46 | display: block; 47 | padding: 1rem; 48 | padding-top: 0.5rem; 49 | padding-bottom: 0.5rem; 50 | font-weight: 600; 51 | color: material-color('grey', '600'); 52 | cursor: pointer; 53 | &:hover { 54 | background: rgb(245, 245, 245); 55 | color: material-color('teal', '600'); 56 | } 57 | &:active { 58 | background: rgb(240, 240, 240); 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/atoms/Selector/Selector.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .selector-wrapper { 4 | position: relative; 5 | .selector { 6 | padding: 0.5rem; 7 | min-width: 7rem; 8 | background: rgba(255,255,255, 0.9); 9 | font-size: 0.9rem; 10 | font-weight: 600; 11 | display: flex; 12 | align-items: center; 13 | border: 1px solid material-color('grey', '400'); 14 | cursor: pointer; 15 | &:hover { 16 | background: white; 17 | border: 1px solid material-color('grey', '500'); 18 | } 19 | svg { 20 | margin-left: auto; 21 | } 22 | } 23 | .options-enter { 24 | animation-duration: .1s; 25 | animation-fill-mode: both; 26 | animation-timing-function: ease-in; 27 | animation-name: popFadeIn; 28 | } 29 | 30 | .options-leave { 31 | animation-duration: .1s; 32 | animation-fill-mode: both; 33 | animation-timing-function: ease-in; 34 | animation-name: popFadeOut; 35 | } 36 | 37 | .options { 38 | width: 120%; 39 | right: 0px; 40 | position: absolute; 41 | background: white; 42 | z-index: 5; 43 | @include media(" { 19 | return ( 20 |
21 |
22 |
23 | 24 |
25 | { !isRegister &&
26 |
27 | 28 | { 29 | user ? ( 30 | 31 | ) : ( 32 | 38 | ) 39 | } 40 |
41 |
} 42 | 43 |
44 |
45 | ); 46 | }; 47 | 48 | export default Header; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/WalletPage/WalletPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './WalletPage.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { PageTemplate, WalletMenu, Card, WalletSubpage, WalletHistorySubpage, WalletProfitSubpage, ResponsiveAd } from 'components'; 5 | import { HeaderContainer, SocketSubscriber } from 'containers'; 6 | import { Route } from 'react-router-dom'; 7 | import { Helmet } from 'react-helmet'; 8 | 9 | const cx = classNames.bind(styles); 10 | 11 | const WalletPage = () => { 12 | return ( 13 | } padding responsive mobileNoPadding> 14 | 15 | 내 지갑 :: Bitimulate 16 | 17 |
18 |
19 | 20 |
21 |
22 | 23 |
24 | 25 |
26 | 27 | 28 | 29 |
30 |
31 |
32 | 33 | 34 |
35 | ); 36 | }; 37 | 38 | export default WalletPage; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/TradeIndex/TradeIndex.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './TradeIndex.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { RateInfoCard, BitcoinInfoCard } from 'components'; 5 | 6 | const cx = classNames.bind(styles); 7 | 8 | const TradeIndex = ({rate, pinMap, onTogglePin, showPinned, krwRate}) => { 9 | let filtered = showPinned ? ( 10 | rate.filter((info) => pinMap[info.get('currencyKey')]) 11 | ) : rate; 12 | const btcInfo = rate.find(info => info.get('currencyName') === 'Bitcoin'); 13 | 14 | 15 | const rateInfoCardList = filtered.map( 16 | (info) => ( 17 | onTogglePin(info.get('currencyKey'))} 25 | pinned={pinMap[info.get('currencyKey')]} 26 | info={info} 27 | /> 28 | ) 29 | ) 30 | return ( 31 |
32 |
33 | { btcInfo && ( 34 | 40 | ) } 41 | {rateInfoCardList} 42 |
43 |
44 | ); 45 | }; 46 | 47 | export default TradeIndex; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/organisms/Sidebar/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Sidebar.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { SidebarWrapper, Button } from 'components'; 5 | import { Link } from 'react-router-dom'; 6 | const cx = classNames.bind(styles); 7 | 8 | const MenuItem = ({ to, children, onClick}) => { 9 | return ({children}) 10 | } 11 | 12 | const Sidebar = ({ 13 | visible, 14 | user, 15 | onLoginClick, 16 | onClose, 17 | onLogout 18 | }) => ( 19 | 20 |
21 | { 22 | user ? [ 23 |
24 | {user.get('displayName')}님,
안녕하세요! 25 |
, 26 | 27 | ] 28 | : [ 29 |
30 | 모의 거래를 지금 시작해보세요! 31 |
, 32 | 35 | ] 36 | } 37 |
38 |
39 | 거래소 40 | { user && 내 지갑} 41 | 랭킹 42 |
43 |
44 | ); 45 | 46 | export default Sidebar; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/TradeDetailSubpage/TradeDetailSubpage.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import styles from './TradeDetailSubpage.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { TradeChartContainer, OrderBookContainer, TradeSectionContainer, TradeHistoryContainer } from 'containers'; 5 | import { Helmet } from 'react-helmet'; 6 | import { ResponsiveAd } from 'components'; 7 | 8 | const cx = classNames.bind(styles); 9 | 10 | class TradeDetailSubpage extends Component { 11 | scrollToTop = () => { 12 | document.documentElement.scrollTop = 0; 13 | } 14 | componentDidMount() { 15 | this.scrollToTop(); 16 | } 17 | componentDidUpdate(prevProps, prevState) { 18 | if(prevProps.match.params.currencyKey !== this.props.match.params.currencyKey) { 19 | this.scrollToTop(); 20 | } 21 | } 22 | 23 | 24 | render() { 25 | const { currencyKey } = this.props.match.params; 26 | 27 | return ( 28 |
29 |
30 | 31 | {`[${currencyKey}] 거래소 :: Bitimulate`} 32 | 33 | 34 |
35 | 36 | 37 | 38 | 39 |
40 | 41 |
42 |
43 | ); 44 | } 45 | } 46 | 47 | 48 | export default TradeDetailSubpage; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/Msgbox/Msgbox.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .msgbox { 4 | @include material-shadow(2, 0.75); 5 | width: 300px; 6 | .head { 7 | color: white; 8 | padding-top: 0.5rem; 9 | padding-bottom: 0.5rem; 10 | font-size: 2rem; 11 | text-align: center; 12 | &.success { 13 | background: material-color('green', '400'); 14 | } 15 | &.error { 16 | background: material-color('red', '400'); 17 | } 18 | } 19 | .content { 20 | background: white; 21 | padding: 1.50rem; 22 | font-size: 1.15rem; 23 | min-height: 6rem; 24 | font-weight: 600; 25 | display: flex; 26 | align-items: center; 27 | white-space: pre; 28 | @include media(" { 38 | return ( 39 |
40 |
41 |
42 | 45 |
46 |
47 | 48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | ); 56 | }; 57 | 58 | export default TradeIndexOptions; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import { HomePage, TradePage, RegisterPage, WalletPage, RankingPage, ReportPage, RewardPage, TermsPage } from 'components'; 4 | import { Route } from 'react-router-dom'; 5 | import { 6 | ScreenMaskContainer, 7 | LoginModalContainer, 8 | UserLoader, 9 | Core 10 | } from 'containers'; 11 | import { Helmet } from 'react-helmet'; 12 | import ReactGA from 'react-ga'; 13 | 14 | function logPageView() { 15 | ReactGA.set({ page: window.location.pathname + window.location.search }); 16 | ReactGA.pageview(window.location.pathname + window.location.search); 17 | } 18 | 19 | class App extends Component { 20 | componentWillReceiveProps(nextProps) { 21 | if(nextProps.location !== this.props.location) { 22 | logPageView(); 23 | } 24 | } 25 | 26 | render() { 27 | return ( 28 |
29 | 30 | Bitimulate - 가상화폐 모의 거래소 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | ); 46 | } 47 | } 48 | 49 | export default App; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/CoinBlock/CoinBlock.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .coin-block-wrapper { 4 | width: 25%; 5 | padding: 0.5rem; 6 | @include media(" { 15 | // this.setState({ 16 | // show: false 17 | // }); 18 | 19 | // setTimeout(() => { 20 | // this.setState({ 21 | // show: true 22 | // }); 23 | // }, 0); 24 | // }, 1000) 25 | 26 | componentDidUpdate(prevProps, prevState) { 27 | if(this.state.show && prevState.show !== this.state.show) { 28 | this.loadAds(); 29 | } 30 | } 31 | 32 | componentDidCatch(error, info) { 33 | // Display fallback UI 34 | this.setState({ show: false }); 35 | } 36 | 37 | 38 | loadAds = () => { 39 | try { 40 | const adsbygoogle = window.adsbygoogle || []; 41 | adsbygoogle.push({}); 42 | } catch (e) { 43 | 44 | } 45 | } 46 | 47 | componentDidMount() { 48 | this.loadAds(); 49 | window.addEventListener('resize', this.handleResize); 50 | } 51 | 52 | componentWillUnmount() { 53 | window.removeEventListener('resize', this.handleResize); 54 | } 55 | 56 | render() { 57 | const { show } = this.state; 58 | return null; 59 | if(!show) return null; 60 | return ( 61 | 62 | ); 63 | } 64 | } 65 | 66 | export default ResponsiveAd; -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/poloniex/currencyPairMap.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '7': 'BTC_BCN', 3 | '8': 'BTC_BELA', 4 | '10': 'BTC_BLK', 5 | '12': 'BTC_BTCD', 6 | '13': 'BTC_BTM', 7 | '14': 'BTC_BTS', 8 | '15': 'BTC_BURST', 9 | '20': 'BTC_CLAM', 10 | '24': 'BTC_DASH', 11 | '25': 'BTC_DGB', 12 | '27': 'BTC_DOGE', 13 | '28': 'BTC_EMC2', 14 | '31': 'BTC_FLDC', 15 | '32': 'BTC_FLO', 16 | '38': 'BTC_GAME', 17 | '40': 'BTC_GRC', 18 | '43': 'BTC_HUC', 19 | '50': 'BTC_LTC', 20 | '51': 'BTC_MAID', 21 | '58': 'BTC_OMNI', 22 | '61': 'BTC_NAV', 23 | '63': 'BTC_NEOS', 24 | '64': 'BTC_NMC', 25 | '69': 'BTC_NXT', 26 | '73': 'BTC_PINK', 27 | '74': 'BTC_POT', 28 | '75': 'BTC_PPC', 29 | '83': 'BTC_RIC', 30 | '89': 'BTC_STR', 31 | '92': 'BTC_SYS', 32 | '97': 'BTC_VIA', 33 | '98': 'BTC_XVC', 34 | '99': 'BTC_VRC', 35 | '100': 'BTC_VTC', 36 | '104': 'BTC_XBC', 37 | '108': 'BTC_XCP', 38 | '112': 'BTC_XEM', 39 | '114': 'BTC_XMR', 40 | '116': 'BTC_XPM', 41 | '117': 'BTC_XRP', 42 | '121': 'USDT_BTC', 43 | '131': 'XMR_BTCD', 44 | '148': 'BTC_ETH', 45 | '150': 'BTC_SC', 46 | '151': 'BTC_BCY', 47 | '153': 'BTC_EXP', 48 | '155': 'BTC_FCT', 49 | '158': 'BTC_RADS', 50 | '160': 'BTC_AMP', 51 | '162': 'BTC_DCR', 52 | '163': 'BTC_LSK', 53 | '167': 'BTC_LBC', 54 | '168': 'BTC_STEEM', 55 | '170': 'BTC_SBD', 56 | '171': 'BTC_ETC', 57 | '174': 'BTC_REP', 58 | '177': 'BTC_ARDR', 59 | '178': 'BTC_ZEC', 60 | '182': 'BTC_STRAT', 61 | '183': 'BTC_NXC', 62 | '184': 'BTC_PASC', 63 | '185': 'BTC_GNT', 64 | '187': 'BTC_GNO', 65 | '189': 'BTC_BCH', 66 | '192': 'BTC_ZRX', 67 | '194': 'BTC_CVC', 68 | '196': 'BTC_OMG', 69 | '198': 'BTC_GAS', 70 | '200': 'BTC_STORJ' 71 | }; -------------------------------------------------------------------------------- /bitimulate-frontend/src/containers/UserLoader.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | // import redux dependencies 3 | import { connect } from 'react-redux'; 4 | import {bindActionCreators} from 'redux'; 5 | import * as userActions from 'store/modules/user'; 6 | import storage from 'lib/storage'; 7 | import socket from 'lib/socket'; 8 | 9 | 10 | class UserLoader extends Component { 11 | 12 | checkLoginStatus = async () => { 13 | const { UserActions } = this.props; 14 | 15 | const user = storage.get('__BTM_USER__'); 16 | 17 | if(user) { 18 | UserActions.setUser(user); 19 | } 20 | 21 | try { 22 | await UserActions.checkLoginStatus(); 23 | await UserActions.getMetaInfo(); 24 | await UserActions.getWallet(); 25 | if(!user || (user && user._id !== this.props.user.get('_id'))) { 26 | // if there is any change in login status, resave the user info 27 | storage.set('__BTM_USER__', this.props.user.toJS()); 28 | } 29 | } catch (e) { 30 | // if there is an error, removes the data from the storage 31 | storage.remove('__BTM_USER__'); 32 | return; 33 | } 34 | } 35 | 36 | componentDidUpdate(prevProps, prevState) { 37 | // recheck login status when userId changes 38 | 39 | if(!prevProps.user && this.props.user) { 40 | this.checkLoginStatus(); 41 | // restart socket userchange 42 | socket.close(); 43 | } 44 | } 45 | 46 | 47 | componentDidMount() { 48 | this.checkLoginStatus(); 49 | } 50 | 51 | render() { 52 | return null; 53 | } 54 | } 55 | 56 | export default connect( 57 | (state) => ({ 58 | user: state.user.get('user'), 59 | }), 60 | (dispatch) => ({ 61 | UserActions: bindActionCreators(userActions, dispatch) 62 | }) 63 | )(UserLoader); 64 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/RankingPage/RankingPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './RankingPage.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { PageTemplate, Card, PolyBackground, ResponsiveAd } from 'components'; 5 | import { HeaderContainer, RankingContainer } from 'containers'; 6 | import { Helmet } from 'react-helmet'; 7 | import { Link } from 'react-router-dom'; 8 | 9 | const cx = classNames.bind(styles); 10 | 11 | const RankingPage = ({match}) => { 12 | const { type } = match.params; 13 | 14 | return ( 15 | } padding> 16 | 17 | 랭킹 :: Bitimulate 18 | 19 | 20 |
21 |
22 | 23 |
24 | 25 |
26 |

수익률 랭킹

27 |
28 | 수익률은 USD를 기반으로 계산됩니다. 29 |
랭킹은 1시간마다 매겨집니다. 30 |
월 수익률 랭킹 1위에겐 상금이 주어집니다! 31 |
32 |
어뷰징이 발견될 시 롤백처리 될 수 있습니다. 33 |
34 |
35 |
36 | 39 | 월 수익률 40 | 41 | 44 | 전체 수익률 45 | 46 |
47 | 48 |
49 |
50 | ); 51 | } 52 | 53 | export default RankingPage; -------------------------------------------------------------------------------- /bitimulate-backend/src/lib/poloniex/index.js: -------------------------------------------------------------------------------- 1 | const currencyPairMap = require('./currencyPairMap'); 2 | const axios = require('axios'); 3 | 4 | module.exports = (function () { 5 | function getChartData(currencyPair, period = 86400, start = 1420070400, retry) { 6 | return axios.get(`https://poloniex.com/public?command=returnChartData¤cyPair=${currencyPair}&start=${start}&end=9999999999&period=${period}`, { timeout: 15000 }).then( 7 | response => response.data 8 | ); 9 | } 10 | 11 | function getCurrencyPairName(id) { 12 | if(id > 193) { 13 | return 'NULL_NULL'; 14 | } 15 | return currencyPairMap[id.toString()]; 16 | } 17 | 18 | function getTickers() { 19 | return axios.get('https://poloniex.com/public?command=returnTicker').then( 20 | response => response.data 21 | ); 22 | }; 23 | 24 | function convertToTickerObject(data) { 25 | const keys = [ 26 | 'id', 27 | 'last', 28 | 'lowestAsk', 29 | 'highestBid', 30 | 'percentChange', 31 | 'baseVolume', 32 | 'quoteVolume', 33 | 'isFrozen', 34 | 'high24hr', 35 | 'low24hr' 36 | ]; 37 | const object = {}; 38 | data.forEach((value, i) => { 39 | // sets the name value 40 | if (i === 0) { 41 | object.name = getCurrencyPairName(value); 42 | return; 43 | } 44 | const key = keys[i]; 45 | object[key] = value; 46 | }); 47 | 48 | return object; 49 | } 50 | 51 | function getOrderBook(currencyPair, depth) { 52 | return axios.get(`https://poloniex.com/public?command=returnOrderBook¤cyPair=${currencyPair}&depth=3`) 53 | .then(response => response.data); 54 | } 55 | 56 | return { 57 | getCurrencyPairName, 58 | getTickers, 59 | convertToTickerObject, 60 | getChartData, 61 | getOrderBook 62 | }; 63 | })(); -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/WalletTable/WalletTable.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .wallet-table { 4 | margin-top: 1rem; 5 | 6 | .table-head { 7 | display: flex; 8 | flex-direction: row; 9 | height: 2.5rem; 10 | align-items: center; 11 | border-bottom: 1px solid material-color('grey', '300'); 12 | .col { 13 | line-height: 1rem; 14 | font-size: 1rem; 15 | font-weight: 700; 16 | color: material-color('grey', '800'); 17 | } 18 | } 19 | .col { 20 | padding: 0.5rem; 21 | &.coin { 22 | flex: 4; 23 | } 24 | &.percent { 25 | font-size: 0.8rem; 26 | text-align: right; 27 | flex: 4.5; 28 | @include media("=medium") { 55 | &:first-child { 56 | padding-left: 0; 57 | } 58 | &:last-child { 59 | padding-right: 0; 60 | } 61 | } 62 | } 63 | 64 | .info-box + .info-box { 65 | @include media(">=medium") { 66 | border-left: 1px solid material-color('grey', '300'); 67 | } 68 | @include media("=medium") { 13 | display: none; 14 | } 15 | } 16 | 17 | // Settings 18 | // ================================================== 19 | $hamburger-padding-x : 0px !default; 20 | $hamburger-padding-y : 0px !default; 21 | $hamburger-layer-width : 30px !default; 22 | $hamburger-layer-height : 4px !default; 23 | $hamburger-layer-spacing : 5px !default; 24 | $hamburger-layer-color : white !default; 25 | $hamburger-layer-border-radius : 1px !default; 26 | $hamburger-hover-opacity : 0.7 !default; 27 | $hamburger-hover-transition-duration : 0.15s !default; 28 | $hamburger-hover-transition-timing-function: linear !default; 29 | 30 | // To use CSS filters as the hover effect instead of opacity, 31 | // set $hamburger-hover-use-filter as true and 32 | // change the value of $hamburger-hover-filter accordingly. 33 | $hamburger-hover-use-filter: false !default; 34 | $hamburger-hover-filter : opacity(50%) !default; 35 | 36 | // Types (Remove or comment out what you don’t need) 37 | // ================================================== 38 | $hamburger-types: ( 39 | elastic, 40 | ) !default; 41 | 42 | :global { 43 | // Base Hamburger (We need this) 44 | // ================================================== 45 | @import "~hamburgers/_sass/hamburgers/base"; 46 | 47 | // Hamburger types 48 | // ================================================== 49 | @import "~hamburgers/_sass/hamburgers/types/elastic"; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/molecules/CurrentInfo/CurrentInfo.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './CurrentInfo.scss'; 3 | import classNames from 'classnames/bind'; 4 | import { LabelBlock } from 'components'; 5 | import moment from 'moment/moment'; 6 | const cx = classNames.bind(styles); 7 | 8 | const CurrentInfo = ({info}) => { 9 | const { 10 | lastUpdate, 11 | last, 12 | low24hr, 13 | high24hr, 14 | highestBid, 15 | lowestAsk, 16 | baseVolume, 17 | percentChange 18 | } = info.toJS(); 19 | 20 | 21 | function limitDigit(value, d = 10) { 22 | const digits = (d - Math.round(Math.log10(value))); 23 | const fixed = value.toFixed(digits > d ? d : digits); 24 | const float = parseFloat(fixed) 25 | if(float > 1000) { 26 | return float.toLocaleString(); 27 | } 28 | return fixed; 29 | } 30 | 31 | return ( 32 |
33 |
34 | 35 | {moment(lastUpdate).format('YYYY MMM DD HH:mm')} 36 | 37 | 38 | {limitDigit(baseVolume)} 39 | 40 | 41 | {limitDigit(last)} 42 | 43 | 44 | {limitDigit(low24hr)} 45 | 46 | 47 | {limitDigit(high24hr)} 48 | 49 | 50 | {limitDigit(lowestAsk)} 51 | 52 | 53 | {limitDigit(highestBid)} 54 | 55 | 56 | {percentChange < 0 ? '' : '+' }{Math.round(percentChange * 10000 57 | ) / 100}% 58 | 59 |
60 | ); 61 | }; 62 | 63 | export default CurrentInfo; -------------------------------------------------------------------------------- /bitimulate-frontend/src/components/pages/RankingPage/RankingPage.scss: -------------------------------------------------------------------------------- 1 | @import 'utils'; 2 | 3 | .block { 4 | background: linear-gradient(to right, #276caf, #30a1c1); 5 | height: 25rem; 6 | z-index: 1; 7 | @include media("