diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 165e4a7..569885c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,8 @@ jobs: cache-dependency-path: frontend/yarn.lock - run: yarn install - run: yarn run eslint - - run: yarn build + - run: APP=maker yarn build + - run: APP=taker yarn build test_daemons: strategy: @@ -68,6 +69,9 @@ jobs: run: | target/debug/maker --data-dir=/tmp/maker --generate-seed & sleep 5s # Wait for maker to start + target/debug/taker --data-dir=/tmp/taker --generate-seed & sleep 5s # Wait for taker to start - curl --fail http://localhost:8000/alive + + curl --fail http://localhost:8000/api/alive + curl --fail http://localhost:8001/api/alive diff --git a/README.md b/README.md index 5f1ceeb..65b4b0d 100644 --- a/README.md +++ b/README.md @@ -27,16 +27,15 @@ Note: The sqlite databases for maker and taker are currently created in the proj ## Starting the maker and taker frontend We use a single react project for hosting both the taker and the maker frontends. - -To start it in development mode: +However, the development environment still needs to be start twice! +Which frontend to start is configured via the `APP` environment variable. ```bash -cd frontend && yarn dev +cd frontend; +APP=taker yarn dev +APP=maker yarn dev ``` -- To access maker: [Maker](http://localhost:3000/maker) -- To access taker: [Taker](http://localhost:3000/taker) - Bundling the web frontend and serving it from the respective daemon is yet to be configured. At the moment you will need a browser extension to allow CORS headers like `CORS Everywhere` ([Firefox Extension](https://addons.mozilla.org/en-US/firefox/addon/cors-everywhere/)) to use the frontends. diff --git a/daemon/src/maker.rs b/daemon/src/maker.rs index b3a14db..a16ea8c 100644 --- a/daemon/src/maker.rs +++ b/daemon/src/maker.rs @@ -146,7 +146,7 @@ async fn main() -> Result<()> { }, )) .mount( - "/", + "/api", rocket::routes![ routes_maker::maker_feed, routes_maker::post_sell_order, diff --git a/daemon/src/routes_maker.rs b/daemon/src/routes_maker.rs index 22af97c..a5f7bab 100644 --- a/daemon/src/routes_maker.rs +++ b/daemon/src/routes_maker.rs @@ -12,7 +12,7 @@ use serde::Deserialize; use tokio::select; use tokio::sync::{mpsc, watch}; -#[rocket::get("/maker-feed")] +#[rocket::get("/feed")] pub async fn maker_feed( rx_cfds: &State>>, rx_order: &State>>, diff --git a/daemon/src/taker.rs b/daemon/src/taker.rs index 9b4b083..1f081fd 100644 --- a/daemon/src/taker.rs +++ b/daemon/src/taker.rs @@ -156,7 +156,7 @@ async fn main() -> Result<()> { }, )) .mount( - "/", + "/api", rocket::routes![ routes_taker::feed, routes_taker::post_cfd, diff --git a/dprint.json b/dprint.json index e383bad..3241e34 100644 --- a/dprint.json +++ b/dprint.json @@ -7,7 +7,7 @@ "wrap_comments": true, "comment_width": 120 }, - "includes": ["**/*.{md,rs,toml}"], + "includes": ["**/*.{md,rs,toml,ts,tsx,js}"], "excludes": ["**/target", "**/sqlx-data.json", "frontend/dist", diff --git a/frontend/dynamicApp.ts b/frontend/dynamicApp.ts new file mode 100644 index 0000000..8161555 --- /dev/null +++ b/frontend/dynamicApp.ts @@ -0,0 +1,18 @@ +import { Plugin } from "vite"; + +export default function dynamicApp(app: string): Plugin { + return { + name: "dynamicApp", // required, will show up in warnings and errors + resolveId: (id) => { + // For some reason these are different? + const productionBuildId = "./__app__.tsx"; + const devBuildId = "/__app__.tsx"; + + if (id === productionBuildId || id === devBuildId) { + return `${__dirname}/${app}.tsx`; + } + + return null; + }, + }; +} diff --git a/frontend/index.html b/frontend/index.html index f71b270..8546184 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,13 +1,14 @@ - - - - - Hermes Main page - - -
- - + + + + + Hermes + + +
+ + diff --git a/frontend/maker.html b/frontend/maker.html deleted file mode 100644 index 6d9dc93..0000000 --- a/frontend/maker.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Hermes Maker - - -
- - - diff --git a/frontend/src/main.tsx b/frontend/maker.tsx similarity index 85% rename from frontend/src/main.tsx rename to frontend/maker.tsx index a0dc897..5c000ef 100644 --- a/frontend/src/main.tsx +++ b/frontend/maker.tsx @@ -3,9 +3,9 @@ import React from "react"; import ReactDOM from "react-dom"; import { BrowserRouter } from "react-router-dom"; import { EventSourceProvider } from "react-sse-hooks"; -import "./index.css"; -import App from "./Maker"; -import theme from "./theme"; +import "./src/index.css"; +import App from "./src/Maker"; +import theme from "./src/theme"; ReactDOM.render( diff --git a/frontend/src/Maker.tsx b/frontend/src/Maker.tsx index a5f966e..18264c5 100644 --- a/frontend/src/Maker.tsx +++ b/frontend/src/Maker.tsx @@ -17,16 +17,13 @@ import { useAsync } from "react-async"; import { Route, Routes } from "react-router-dom"; import { useEventSource } from "react-sse-hooks"; import "./App.css"; -import OrderTile from "./components/OrderTile"; import CfdTile from "./components/CfdTile"; import CurrencyInputField from "./components/CurrencyInputField"; import useLatestEvent from "./components/Hooks"; import NavLink from "./components/NavLink"; +import OrderTile from "./components/OrderTile"; import { Cfd, Order } from "./components/Types"; -/* TODO: Change from localhost:8001 */ -const BASE_URL = "http://localhost:8001"; - interface CfdSellOrderPayload { price: number; min_quantity: number; @@ -34,7 +31,7 @@ interface CfdSellOrderPayload { } async function postCfdSellOrderRequest(payload: CfdSellOrderPayload) { - let res = await axios.post(BASE_URL + `/order/sell`, JSON.stringify(payload)); + let res = await axios.post(`/api/order/sell`, JSON.stringify(payload)); if (!res.status.toString().startsWith("2")) { console.log("Status: " + res.status + ", " + res.statusText); @@ -43,7 +40,7 @@ async function postCfdSellOrderRequest(payload: CfdSellOrderPayload) { } export default function App() { - let source = useEventSource({ source: BASE_URL + "/maker-feed" }); + let source = useEventSource({ source: "/api/feed" }); const cfds = useLatestEvent(source, "cfds"); const order = useLatestEvent(source, "order"); diff --git a/frontend/src/Taker.tsx b/frontend/src/Taker.tsx index 089d31e..acbea83 100644 --- a/frontend/src/Taker.tsx +++ b/frontend/src/Taker.tsx @@ -11,9 +11,6 @@ import useLatestEvent from "./components/Hooks"; import NavLink from "./components/NavLink"; import { Cfd, Order } from "./components/Types"; -/* TODO: Change from localhost:8000 */ -const BASE_URL = "http://localhost:8000"; - interface CfdTakeRequestPayload { order_id: string; quantity: number; @@ -30,7 +27,7 @@ interface MarginResponse { } async function postCfdTakeRequest(payload: CfdTakeRequestPayload) { - let res = await axios.post(BASE_URL + `/cfd`, JSON.stringify(payload)); + let res = await axios.post(`/api/cfd`, JSON.stringify(payload)); if (!res.status.toString().startsWith("2")) { throw new Error("failed to create new CFD take request: " + res.status + ", " + res.statusText); @@ -38,7 +35,7 @@ async function postCfdTakeRequest(payload: CfdTakeRequestPayload) { } async function getMargin(payload: MarginRequestPayload): Promise { - let res = await axios.post(BASE_URL + `/calculate/margin`, JSON.stringify(payload)); + let res = await axios.post(`/api/calculate/margin`, JSON.stringify(payload)); if (!res.status.toString().startsWith("2")) { throw new Error("failed to create new CFD take request: " + res.status + ", " + res.statusText); @@ -48,7 +45,7 @@ async function getMargin(payload: MarginRequestPayload): Promise } export default function App() { - let source = useEventSource({ source: BASE_URL + "/feed" }); + let source = useEventSource({ source: "/api/feed" }); const cfds = useLatestEvent(source, "cfds"); const order = useLatestEvent(source, "order"); @@ -152,7 +149,7 @@ export default function App() { Quantity: { - setQuantity(parse(valueString)) + setQuantity(parse(valueString)); if (!order) { return; @@ -162,8 +159,8 @@ export default function App() { let payload: MarginRequestPayload = { leverage: order.leverage, price: order.price, - quantity - } + quantity, + }; calculateMargin(payload); }} value={format(quantity)} diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx deleted file mode 100644 index dc2746c..0000000 --- a/frontend/src/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { ChakraProvider } from "@chakra-ui/react"; -import React from "react"; -import ReactDOM from "react-dom"; -import { BrowserRouter, Route, Routes } from "react-router-dom"; -import { EventSourceProvider } from "react-sse-hooks"; -import "./index.css"; -import Maker from "./Maker"; -import Taker from "./Taker"; -import theme from "./theme"; - -ReactDOM.render( - - - - - - } /> - } /> - - - - - , - document.getElementById("root"), -); diff --git a/frontend/src/main_maker.tsx b/frontend/src/main_maker.tsx deleted file mode 100644 index a90ce15..0000000 --- a/frontend/src/main_maker.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { ChakraProvider } from "@chakra-ui/react"; -import React from "react"; -import ReactDOM from "react-dom"; -import { BrowserRouter } from "react-router-dom"; -import { EventSourceProvider } from "react-sse-hooks"; -import "./index.css"; -import Maker from "./Maker"; -import theme from "./theme"; - -ReactDOM.render( - - - - - - - - - , - document.getElementById("root"), -); diff --git a/frontend/taker.html b/frontend/taker.html deleted file mode 100644 index f15130b..0000000 --- a/frontend/taker.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Hermes Taker - - -
- - - diff --git a/frontend/src/main_taker.tsx b/frontend/taker.tsx similarity index 81% rename from frontend/src/main_taker.tsx rename to frontend/taker.tsx index daba5be..7076c3a 100644 --- a/frontend/src/main_taker.tsx +++ b/frontend/taker.tsx @@ -3,16 +3,16 @@ import React from "react"; import ReactDOM from "react-dom"; import { BrowserRouter } from "react-router-dom"; import { EventSourceProvider } from "react-sse-hooks"; -import "./index.css"; -import Taker from "./Taker"; -import theme from "./theme"; +import "./src/index.css"; +import App from "./src/Taker"; +import theme from "./src/theme"; ReactDOM.render( - + diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 6414703..22a4d8d 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -16,5 +16,5 @@ "noEmit": true, "jsx": "react" }, - "include": ["./src"] + "include": ["."] } diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index b531056..3003308 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,7 +1,18 @@ -import { resolve } from "path"; - import reactRefresh from "@vitejs/plugin-react-refresh"; +import { resolve } from "path"; import { defineConfig } from "vite"; +import dynamicApp from "./dynamicApp"; + +const app = process.env.APP; + +if (!app || (app !== "maker" && app !== "taker")) { + throw new Error("APP environment variable needs to be set to `maker` `taker`"); +} + +const backendPorts = { + "taker": 8000, + "maker": 8001, +}; // https://vitejs.dev/config/ export default defineConfig({ @@ -11,40 +22,17 @@ export default defineConfig({ ? [reactRefresh()] : [] ), + dynamicApp(app), ], build: { rollupOptions: { - input: { - maker: resolve(__dirname, "maker.html"), - taker: resolve(__dirname, "taker.html"), - }, + input: resolve(__dirname, `index.html`), }, + outDir: `dist/${app}`, }, server: { - open: "/maker", + proxy: { + "/api": `http://localhost:${backendPorts[app]}`, + }, }, - // server: { - // proxy: { - // '/foo': 'http://localhost:4567', - // '/api': { - // target: 'http://jsonplaceholder.typicode.com', - // changeOrigin: true, - // rewrite: (path) => path.replace(/^\/api/, '') - // }, - // // with RegEx - // '^/fallback/.*': { - // target: 'http://jsonplaceholder.typicode.com', - // changeOrigin: true, - // rewrite: (path) => path.replace(/^\/fallback/, '') - // }, - // // Using the proxy instance - // '/api': { - // target: 'http://jsonplaceholder.typicode.com', - // changeOrigin: true, - // configure: (proxy, options) => { - // // proxy will be an instance of 'http-proxy' - // } - // } - // } - // } });