kopia lustrzana https://github.com/bugout-dev/moonstream
Merge branch 'main' into crawlers-access-id
commit
a25e149e1d
|
@ -6,6 +6,7 @@ on:
|
|||
- "main"
|
||||
paths:
|
||||
- "backend/**"
|
||||
- "!backend/deploy/**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
|
@ -6,6 +6,7 @@ on:
|
|||
- "main"
|
||||
paths:
|
||||
- "crawlers/**"
|
||||
- "!crawlers/deploy/**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
|
@ -6,6 +6,7 @@ on:
|
|||
- "main"
|
||||
paths:
|
||||
- "db/**"
|
||||
- "!db/deploy/**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
|
@ -25,7 +25,7 @@ CANONICAL_SUBSCRIPTION_TYPES = {
|
|||
icon_url="https://s3.amazonaws.com/static.simiotics.com/moonstream/assets/ethereum/eth-diamond-purple.png",
|
||||
stripe_product_id=None,
|
||||
stripe_price_id=None,
|
||||
active=False,
|
||||
active=True,
|
||||
),
|
||||
"polygon_smartcontract": SubscriptionTypeResourceData(
|
||||
id="polygon_smartcontract",
|
||||
|
|
|
@ -8,4 +8,5 @@ User=ubuntu
|
|||
Group=www-data
|
||||
WorkingDirectory=/home/ubuntu/moonstream/crawlers/mooncrawl
|
||||
EnvironmentFile=/home/ubuntu/moonstream-secrets/app.env
|
||||
ExecStart=/home/ubuntu/moonstream-env/bin/python -m mooncrawl.crawler blocks missing --blockchain ethereum -n
|
||||
ExecStart=/home/ubuntu/moonstream-env/bin/python -m mooncrawl.crawler blocks missing --blockchain ethereum -n
|
||||
SyslogIdentifier=ethereum-missing
|
||||
|
|
|
@ -8,4 +8,5 @@ User=ubuntu
|
|||
Group=www-data
|
||||
WorkingDirectory=/home/ubuntu/moonstream/crawlers/mooncrawl
|
||||
EnvironmentFile=/home/ubuntu/moonstream-secrets/app.env
|
||||
ExecStart=/home/ubuntu/moonstream-env/bin/python -m mooncrawl.crawler trending
|
||||
ExecStart=/home/ubuntu/moonstream-env/bin/python -m mooncrawl.crawler trending
|
||||
SyslogIdentifier=ethereum-trending
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
[Unit]
|
||||
Description=Ethereum txpool crawler
|
||||
After=network.target
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=3
|
||||
|
||||
[Service]
|
||||
User=ubuntu
|
||||
Group=www-data
|
||||
WorkingDirectory=/home/ubuntu/moonstream/crawlers/txpool
|
||||
EnvironmentFile=/home/ubuntu/moonstream-secrets/app.env
|
||||
Restart=on-failure
|
||||
RestartSec=15s
|
||||
ExecStart=/home/ubuntu/moonstream/crawlers/txpool/txpool -blockchain ethereum -access-id "${NB_CONTROLLER_ACCESS_ID}"
|
||||
SyslogIdentifier=ethereum-txpool
|
||||
|
||||
|
|
|
@ -8,4 +8,5 @@ User=ubuntu
|
|||
Group=www-data
|
||||
WorkingDirectory=/home/ubuntu/moonstream/crawlers/mooncrawl
|
||||
EnvironmentFile=/home/ubuntu/moonstream-secrets/app.env
|
||||
ExecStart=/home/ubuntu/moonstream-env/bin/python -m mooncrawl.crawler blocks missing --blockchain polygon -n
|
||||
ExecStart=/home/ubuntu/moonstream-env/bin/python -m mooncrawl.crawler blocks missing --blockchain polygon -n
|
||||
SyslogIdentifier=polygon-missing
|
||||
|
|
|
@ -9,3 +9,4 @@ Group=www-data
|
|||
WorkingDirectory=/home/ubuntu/moonstream/crawlers/mooncrawl
|
||||
EnvironmentFile=/home/ubuntu/moonstream-secrets/app.env
|
||||
ExecStart=/home/ubuntu/moonstream-env/bin/python -m mooncrawl.stats_worker.dashboard generate --blockchain polygon
|
||||
SyslogIdentifier=polygon-statistics
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
[Unit]
|
||||
Description=Polygon txpool crawler
|
||||
After=network.target
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=3
|
||||
|
||||
[Service]
|
||||
User=ubuntu
|
||||
Group=www-data
|
||||
WorkingDirectory=/home/ubuntu/moonstream/crawlers/txpool
|
||||
EnvironmentFile=/home/ubuntu/moonstream-secrets/app.env
|
||||
Restart=on-failure
|
||||
RestartSec=15s
|
||||
ExecStart=/home/ubuntu/moonstream/crawlers/txpool/txpool -blockchain polygon -access-id "${NB_CONTROLLER_ACCESS_ID}"
|
||||
SyslogIdentifier=polygon-txpool
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ def main() -> None:
|
|||
"--max-blocks-batch",
|
||||
"-m",
|
||||
type=int,
|
||||
default=100,
|
||||
default=80,
|
||||
help="Maximum number of blocks to crawl in a single batch",
|
||||
)
|
||||
|
||||
|
@ -159,7 +159,7 @@ def main() -> None:
|
|||
"--min-blocks-batch",
|
||||
"-n",
|
||||
type=int,
|
||||
default=40,
|
||||
default=20,
|
||||
help="Minimum number of blocks to crawl in a single batch",
|
||||
)
|
||||
|
||||
|
@ -175,7 +175,7 @@ def main() -> None:
|
|||
"--min-sleep-time",
|
||||
"-t",
|
||||
type=float,
|
||||
default=0.01,
|
||||
default=0.1,
|
||||
help="Minimum time to sleep between crawl step",
|
||||
)
|
||||
|
||||
|
@ -191,7 +191,7 @@ def main() -> None:
|
|||
"--new-jobs-refetch-interval",
|
||||
"-r",
|
||||
type=float,
|
||||
default=120,
|
||||
default=180,
|
||||
help="Time to wait before refetching new jobs",
|
||||
)
|
||||
|
||||
|
|
|
@ -172,14 +172,14 @@ def continuous_crawler(
|
|||
)
|
||||
last_heartbeat_time = datetime.utcnow()
|
||||
blocks_cache: Dict[int, int] = {}
|
||||
|
||||
current_sleep_time = min_sleep_time
|
||||
failed_count = 0
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
# query db with limit 1, to avoid session closing
|
||||
db_session.execute("SELECT 1")
|
||||
time.sleep(min_sleep_time)
|
||||
time.sleep(current_sleep_time)
|
||||
|
||||
end_block = min(
|
||||
web3.eth.blockNumber - confirmations,
|
||||
|
@ -187,12 +187,12 @@ def continuous_crawler(
|
|||
)
|
||||
|
||||
if start_block + min_blocks_batch > end_block:
|
||||
min_sleep_time += 0.1
|
||||
current_sleep_time += 0.1
|
||||
logger.info(
|
||||
f"Sleeping for {min_sleep_time} seconds because of low block count"
|
||||
f"Sleeping for {current_sleep_time} seconds because of low block count"
|
||||
)
|
||||
continue
|
||||
min_sleep_time = max(0, min_sleep_time - 0.1)
|
||||
current_sleep_time = max(min_sleep_time, current_sleep_time - 0.1)
|
||||
|
||||
logger.info(f"Crawling events from {start_block} to {end_block}")
|
||||
all_events = _crawl_events(
|
||||
|
|
|
@ -206,18 +206,27 @@ def make_function_call_crawl_jobs(
|
|||
"""
|
||||
|
||||
crawl_job_by_address: Dict[str, FunctionCallCrawlJob] = {}
|
||||
method_signature_by_address: Dict[str, List[str]] = {}
|
||||
|
||||
for entry in entries:
|
||||
contract_address = Web3().toChecksumAddress(_get_tag(entry, "address"))
|
||||
abi = cast(str, entry.content)
|
||||
abi = json.loads(cast(str, entry.content))
|
||||
method_signature = encode_function_signature(abi)
|
||||
if method_signature is None:
|
||||
raise ValueError(f"{abi} is not a function ABI")
|
||||
if contract_address not in crawl_job_by_address:
|
||||
crawl_job_by_address[contract_address] = FunctionCallCrawlJob(
|
||||
contract_abi=[json.loads(abi)],
|
||||
contract_abi=[abi],
|
||||
contract_address=contract_address,
|
||||
created_at=int(datetime.fromisoformat(entry.created_at).timestamp()),
|
||||
)
|
||||
method_signature_by_address[contract_address] = [method_signature]
|
||||
|
||||
else:
|
||||
crawl_job_by_address[contract_address].contract_abi.append(json.loads(abi))
|
||||
|
||||
if method_signature not in method_signature_by_address[contract_address]:
|
||||
crawl_job_by_address[contract_address].contract_abi.append(abi)
|
||||
method_signature_by_address[contract_address].append(method_signature)
|
||||
|
||||
return [crawl_job for crawl_job in crawl_job_by_address.values()]
|
||||
|
||||
|
|
|
@ -91,6 +91,3 @@ def function_call_crawler(
|
|||
i,
|
||||
min(i + batch_size - 1, end_block),
|
||||
)
|
||||
logger.info(f"Crawled {len(crawled_functions)} functions")
|
||||
for function_call in crawled_functions:
|
||||
print(function_call)
|
||||
|
|
|
@ -186,3 +186,7 @@ alembic.dev.ini
|
|||
alembic.prod.ini
|
||||
alembic.moonstreamdb.ini
|
||||
alembic.docker.ini
|
||||
|
||||
# Schematic
|
||||
srv/
|
||||
.schematic.env
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
"""Added index for address type and name of event
|
||||
|
||||
Revision ID: 5f5b8f19570f
|
||||
Revises: f991fc7493c8
|
||||
Create Date: 2022-05-04 11:32:42.309322
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "5f5b8f19570f"
|
||||
down_revision = "f991fc7493c8"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.execute(
|
||||
"""
|
||||
CREATE INDEX ix_polygon_labels_address_label_label_data_type_and_name ON polygon_labels USING BTREE (address,label,(label_data->>'type'),(label_data->>'name'));
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.execute(
|
||||
"""
|
||||
DROP INDEX ix_polygon_labels_address_label_label_data_type_and_name;
|
||||
"""
|
||||
)
|
|
@ -260,6 +260,15 @@ class PolygonLabel(Base): # type: ignore
|
|||
DateTime(timezone=True), server_default=utcnow(), nullable=False
|
||||
)
|
||||
|
||||
# Undescribed indexes
|
||||
"""
|
||||
Migration: alembic\versions\5f5b8f19570f_added_index_for_address_type_and_name_.py
|
||||
|
||||
Index: "ix_polygon_labels_address_label_label_data_type_and_name" created manually.
|
||||
By fields: (address, label, (label_data->>'type'), (label_data->>'name'))
|
||||
Reason: https://github.com/sqlalchemy/alembic/issues/469#issuecomment-441887478
|
||||
"""
|
||||
|
||||
|
||||
class ESDFunctionSignature(Base): # type: ignore
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
with builtins;
|
||||
let
|
||||
scm_repos = [
|
||||
(getEnv "SCM_GIT")
|
||||
(fetchGit {
|
||||
url = "git@gitlab.com:deltaex/schematic.git";
|
||||
rev = "0d9227335ad83e0ed9a62d82375c1e85aadcc08d";
|
||||
})
|
||||
];
|
||||
scm_repo = head (filter (x: x != "") scm_repos);
|
||||
scm = (import scm_repo {
|
||||
verbose = true;
|
||||
repos = [
|
||||
"."
|
||||
(getEnv "MDP_GIT")
|
||||
(fetchGit {
|
||||
url = "git@gitlab.com:mixrank/mdp.git";
|
||||
rev = "76707e9d08178633471fa3c98ef1b08e1e7bbb1c";
|
||||
})
|
||||
] ++ scm_repos;
|
||||
});
|
||||
in rec {
|
||||
schematic = scm.shell.overrideAttrs ( oldAttrs : {
|
||||
shellHook = oldAttrs.shellHook + ''
|
||||
[ -n "$ENV" -a "$ENV" != "dev" ] && export BUGSNAG=2b987ca13cd93a4931bb746aace204fb
|
||||
'';
|
||||
});
|
||||
}
|
|
@ -4,6 +4,8 @@ type PingResponse struct {
|
|||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type BlockNumberResponse struct {
|
||||
BlockNumber uint64 `json:"block_number"`
|
||||
type BlockLatestResponse struct {
|
||||
EthereumBlockLatest uint64 `json:"ethereum_block_latest"`
|
||||
PolygonBlockLatest uint64 `json:"polygon_block_latest"`
|
||||
PolygonBlockLatestLabelsMoonwormAlpha uint64 `json:"polygon_block_latest_label_moonworm_alpha"`
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func InitDB() *sql.DB {
|
||||
db, err := sql.Open("postgres", settings.MOONSTREAM_DB_URI)
|
||||
db, err := sql.Open("postgres", settings.MOONSTREAM_DB_URI_READ_ONLY)
|
||||
if err != nil {
|
||||
// DSN parse error or another initialization error
|
||||
log.Fatal(err)
|
||||
|
|
|
@ -17,18 +17,40 @@ func pingRoute(w http.ResponseWriter, req *http.Request) {
|
|||
func (es *extendedServer) blocksLatestRoute(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
var latestBlock BlockNumberResponse
|
||||
row := es.db.QueryRow("SELECT block_number FROM ethereum_blocks ORDER BY block_number DESC LIMIT 1")
|
||||
err := row.Scan(&latestBlock.BlockNumber)
|
||||
var blockNumbers []uint64
|
||||
var blockLatest BlockLatestResponse
|
||||
rows, err := es.db.Query(`(SELECT block_number FROM ethereum_blocks ORDER BY block_number DESC LIMIT 1)
|
||||
UNION ALL
|
||||
(SELECT block_number FROM polygon_blocks ORDER BY block_number DESC LIMIT 1)
|
||||
UNION ALL
|
||||
(SELECT block_number FROM polygon_labels WHERE label = 'moonworm-alpha' ORDER BY block_number DESC LIMIT 1)`)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
http.Error(w, "Row not found", http.StatusNotFound)
|
||||
} else {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
}
|
||||
log.Printf("An error occurred during sql operation: %s", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
json.NewEncoder(w).Encode(latestBlock)
|
||||
for rows.Next() {
|
||||
var bn uint64
|
||||
err := rows.Scan(&bn)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
http.Error(w, "Row not found", http.StatusNotFound)
|
||||
} else {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
}
|
||||
log.Printf("An error occurred during scan sql response: %s", err)
|
||||
return
|
||||
}
|
||||
blockNumbers = append(blockNumbers, bn)
|
||||
}
|
||||
|
||||
blockLatest = BlockLatestResponse{
|
||||
EthereumBlockLatest: blockNumbers[0],
|
||||
PolygonBlockLatest: blockNumbers[1],
|
||||
PolygonBlockLatestLabelsMoonwormAlpha: blockNumbers[2],
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(blockLatest)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
// Database configs
|
||||
var MOONSTREAM_DB_MAX_IDLE_CONNS int = 30
|
||||
var MOONSTREAM_DB_CONN_MAX_LIFETIME = 30 * time.Minute
|
||||
var MOONSTREAM_DB_URI = os.Getenv("MOONSTREAM_DB_URI")
|
||||
var MOONSTREAM_DB_URI_READ_ONLY = os.Getenv("MOONSTREAM_DB_URI_READ_ONLY")
|
||||
|
||||
// CORS
|
||||
var MOONSTREAM_CORS_ALLOWED_ORIGINS = os.Getenv("MOONSTREAM_CORS_ALLOWED_ORIGINS")
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
# Expects access to Python environment with the requirements for this project installed.
|
||||
# Compile application and run with provided arguments
|
||||
set -e
|
||||
|
||||
MOONSTREAM_DB_SERVER_HOST="${MOONSTREAM_DB_SERVER_HOST:-0.0.0.0}"
|
||||
MOONSTREAM_DB_SERVER_PORT="${MOONSTREAM_DB_SERVER_PORT:-8080}"
|
||||
PROGRAM_NAME="moonstreamdb"
|
||||
|
||||
go run main.go -host "${MOONSTREAM_DB_SERVER_HOST}" -port "${MOONSTREAM_DB_SERVER_PORT}"
|
||||
go build -o "$PROGRAM_NAME" .
|
||||
|
||||
./"$PROGRAM_NAME" "$@"
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
export MOONSTREAM_DB_URI="postgresql://<username>:<password>@<db_host>:<db_port>/<db_name>"
|
||||
export MOONSTREAM_DB_URI_READ_ONLY="postgresql://<username>:<password>@<db_host>:<db_port>/<db_name>"
|
||||
export MOONSTREAM_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://moonstream.to,https://www.moonstream.to,https://alpha.moonstream.to"
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
- namespace: public
|
||||
tablename: alembic_exploration_version
|
||||
included: false
|
||||
- namespace: public
|
||||
tablename: esd_event_signatures
|
||||
- namespace: public
|
||||
tablename: esd_function_signatures
|
||||
- namespace: public
|
||||
tablename: ethereum_blocks
|
||||
- namespace: public
|
||||
tablename: ethereum_labels
|
||||
- namespace: public
|
||||
tablename: ethereum_labels_v1
|
||||
included: false
|
||||
- namespace: public
|
||||
tablename: ethereum_transactions
|
||||
- namespace: public
|
||||
tablename: opensea_crawler_state
|
||||
- namespace: public
|
||||
tablename: polygon_blocks
|
||||
- namespace: public
|
||||
tablename: polygon_labels
|
||||
- namespace: public
|
||||
tablename: polygon_transactions
|
|
@ -45,7 +45,7 @@ export default class MyDocument extends Document {
|
|||
{/* <!-- Global site tag (gtag.js) - Google Analytics --> */}
|
||||
<script
|
||||
async
|
||||
src="https://www.googletagmanager.com/gtag/js?id=G-MNVHX36LZ1"
|
||||
src="https://www.googletagmanager.com/gtag/js?id=UA-156911549-2"
|
||||
></script>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
|
@ -55,7 +55,7 @@ export default class MyDocument extends Document {
|
|||
dataLayer.push(arguments);
|
||||
}
|
||||
gtag("js", new Date());
|
||||
gtag("config", "G-MNVHX36LZ1");`,
|
||||
gtag("config", "UA-156911549-2");`,
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import React from "react";
|
||||
import { useRouter } from "../src/core/hooks";
|
||||
|
||||
const DiscordLeed = () => {
|
||||
const router = useRouter();
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
router.push("https://discord.gg/K56VNUQGvA");
|
||||
}, [router]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export default DiscordLeed;
|
|
@ -8,7 +8,7 @@ const Docs = () => {
|
|||
return (
|
||||
// <Box overflowY="hidden" w="100%" maxH="100%" minH="100vh">
|
||||
<>
|
||||
<Box w="100%" maxH="100vh" overflowY="scroll">
|
||||
<Box w="100%" maxH="100vh" overflowY="scroll" zIndex={0}>
|
||||
<RedocStandalone
|
||||
specUrl="https://api.moonstream.to/openapi.json"
|
||||
options={{
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -1,57 +1,47 @@
|
|||
import React, { useContext } from "react";
|
||||
import React from "react";
|
||||
import { useStatus } from "../../src/core/hooks";
|
||||
import { Heading, Text, Flex, Spacer, chakra, Spinner } from "@chakra-ui/react";
|
||||
import { getLayout, getLayoutProps } from "../../src/layouts/InfoPageLayout";
|
||||
import UserContext from "../../src/core/providers/UserProvider/context";
|
||||
|
||||
const Status = () => {
|
||||
const user = useContext(UserContext);
|
||||
const healthyStatusText = "Available";
|
||||
const downStatusText = "Unavailable";
|
||||
const unauthorizedText = "Please login";
|
||||
const healthyStatusColor = "green.900";
|
||||
const downStatusColor = "red.600";
|
||||
|
||||
const shortTimestamp = (rawTimestamp) => {
|
||||
return rawTimestamp.replace(/^.+T/, "").replace(/\..+/, "");
|
||||
};
|
||||
const { serverListStatusCache } = useStatus();
|
||||
|
||||
const {
|
||||
serverListStatusCache,
|
||||
crawlersStatusCache,
|
||||
dbServerStatusCache,
|
||||
latestBlockDBStatusCache,
|
||||
} = useStatus();
|
||||
console.log(serverListStatusCache?.data);
|
||||
|
||||
const moonstreamapiStatus = serverListStatusCache?.data?.filter(
|
||||
(i) => i.status.name === "moonstreamapi"
|
||||
(i) => i.name === "moonstream_api"
|
||||
)[0];
|
||||
const moonstreamCrawlersStatus = serverListStatusCache?.data?.filter(
|
||||
(i) => i.status.name === "moonstream_crawlers"
|
||||
(i) => i.name === "moonstream_crawlers"
|
||||
)[0];
|
||||
const nodeBalacerStatus = serverListStatusCache?.data?.filter(
|
||||
(i) => i.name === "moonstream_node_balancer"
|
||||
)[0];
|
||||
const nodeEthereumAStatus = serverListStatusCache?.data?.filter(
|
||||
(i) => i.status.name === "node_ethereum_a"
|
||||
)[0];
|
||||
const nodeEthereumAGeth = serverListStatusCache?.data?.filter(
|
||||
(i) => i.status.name === "node_ethereum_a_geth"
|
||||
(i) => i.name === "node_ethereum_a"
|
||||
)[0];
|
||||
const nodeEthereumBStatus = serverListStatusCache?.data?.filter(
|
||||
(i) => i.status.name === "node_ethereum_b"
|
||||
)[0];
|
||||
const nodeEthereumBGeth = serverListStatusCache?.data?.filter(
|
||||
(i) => i.status.name === "node_ethereum_b_geth"
|
||||
(i) => i.name === "node_ethereum_b"
|
||||
)[0];
|
||||
const nodePolygonAStatus = serverListStatusCache?.data?.filter(
|
||||
(i) => i.status.name === "node_polygon_a"
|
||||
)[0];
|
||||
const nodePolygonAGeth = serverListStatusCache?.data?.filter(
|
||||
(i) => i.status.name === "node_polygon_a_bor"
|
||||
(i) => i.name === "node_polygon_a"
|
||||
)[0];
|
||||
const nodePolygonBStatus = serverListStatusCache?.data?.filter(
|
||||
(i) => i.status.name === "node_polygon_b"
|
||||
(i) => i.name === "node_polygon_b"
|
||||
)[0];
|
||||
const nodePolygonBGeth = serverListStatusCache?.data?.filter(
|
||||
(i) => i.status.name === "node_polygon_b_bor"
|
||||
const dbServerStatus = serverListStatusCache?.data?.filter(
|
||||
(i) => i.name === "moonstream_database"
|
||||
)[0];
|
||||
const dbReplicaServerStatus = serverListStatusCache?.data?.filter(
|
||||
(i) => i.name === "moonstream_database_replica"
|
||||
)[0];
|
||||
const unimLeaderboardStatus = serverListStatusCache?.data?.filter(
|
||||
(i) => i.name === "unim_leaderboard"
|
||||
)[0];
|
||||
|
||||
const StatusRow = (props) => {
|
||||
|
@ -80,12 +70,12 @@ const Status = () => {
|
|||
<StatusRow title="Backend server" cache={serverListStatusCache}>
|
||||
<Text
|
||||
color={
|
||||
moonstreamapiStatus?.status.body.status == "ok"
|
||||
moonstreamapiStatus?.status_code == 200
|
||||
? healthyStatusColor
|
||||
: downStatusColor
|
||||
}
|
||||
>
|
||||
{moonstreamapiStatus?.status.body.status == "ok"
|
||||
{moonstreamapiStatus?.status_code == 200
|
||||
? healthyStatusText
|
||||
: downStatusText}
|
||||
</Text>
|
||||
|
@ -93,42 +83,33 @@ const Status = () => {
|
|||
|
||||
<br />
|
||||
|
||||
<StatusRow title="Crawlers server" cache={crawlersStatusCache}>
|
||||
<StatusRow title="Crawlers server" cache={serverListStatusCache}>
|
||||
<Text
|
||||
color={
|
||||
moonstreamCrawlersStatus?.status.body.status == "ok"
|
||||
moonstreamCrawlersStatus?.status_code == 200
|
||||
? healthyStatusColor
|
||||
: downStatusColor
|
||||
}
|
||||
>
|
||||
{moonstreamCrawlersStatus?.status.body.status == "ok"
|
||||
{moonstreamCrawlersStatus?.status_code == 200
|
||||
? healthyStatusText
|
||||
: downStatusText}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
<StatusRow title="Txpool latest record ts" cache={crawlersStatusCache}>
|
||||
<Text>
|
||||
{!user
|
||||
? crawlersStatusCache?.data?.ethereum_txpool_timestamp
|
||||
? shortTimestamp(
|
||||
crawlersStatusCache?.data?.ethereum_txpool_timestamp
|
||||
)
|
||||
: downStatusText
|
||||
: unauthorizedText}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
<StatusRow
|
||||
title="Trending latest record ts"
|
||||
cache={crawlersStatusCache}
|
||||
>
|
||||
<Text>
|
||||
{!user
|
||||
? crawlersStatusCache?.data?.ethereum_trending_timestamp
|
||||
? shortTimestamp(
|
||||
crawlersStatusCache?.data?.ethereum_trending_timestamp
|
||||
)
|
||||
: downStatusText
|
||||
: unauthorizedText}
|
||||
|
||||
<br />
|
||||
|
||||
<StatusRow title="Node balancer server" cache={serverListStatusCache}>
|
||||
<Text
|
||||
color={
|
||||
nodeBalacerStatus?.status_code == 200
|
||||
? healthyStatusColor
|
||||
: downStatusColor
|
||||
}
|
||||
>
|
||||
{nodeBalacerStatus?.status_code == 200
|
||||
? healthyStatusText
|
||||
: downStatusText}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
|
||||
|
@ -137,20 +118,20 @@ const Status = () => {
|
|||
<StatusRow title="Node Ethereum A" cache={serverListStatusCache}>
|
||||
<Text
|
||||
color={
|
||||
nodeEthereumAStatus?.status.body.status == "ok"
|
||||
nodeEthereumAStatus?.status_code == 200
|
||||
? healthyStatusColor
|
||||
: downStatusColor
|
||||
}
|
||||
>
|
||||
{nodeEthereumAStatus?.status.body.status == "ok"
|
||||
{nodeEthereumAStatus?.status_code == 200
|
||||
? healthyStatusText
|
||||
: downStatusText}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
<StatusRow title="Current block" cache={serverListStatusCache}>
|
||||
<Text>
|
||||
{nodeEthereumAGeth?.status.body.current_block
|
||||
? nodeEthereumAGeth.status.body.current_block
|
||||
{nodeEthereumAStatus?.response?.current_block
|
||||
? nodeEthereumAStatus.response.current_block
|
||||
: 0}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
|
@ -158,20 +139,20 @@ const Status = () => {
|
|||
<StatusRow title="Node Ethereum B" cache={serverListStatusCache}>
|
||||
<Text
|
||||
color={
|
||||
nodeEthereumBStatus?.status.body.status == "ok"
|
||||
nodeEthereumBStatus?.status_code == 200
|
||||
? healthyStatusColor
|
||||
: downStatusColor
|
||||
}
|
||||
>
|
||||
{nodeEthereumBStatus?.status.body.status == "ok"
|
||||
{nodeEthereumBStatus?.status_code == 200
|
||||
? healthyStatusText
|
||||
: downStatusText}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
<StatusRow title="Current block" cache={serverListStatusCache}>
|
||||
<Text>
|
||||
{nodeEthereumBGeth?.status.body.current_block
|
||||
? nodeEthereumBGeth.status.body.current_block
|
||||
{nodeEthereumBStatus?.response?.current_block
|
||||
? nodeEthereumBStatus.response.current_block
|
||||
: 0}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
|
@ -179,20 +160,20 @@ const Status = () => {
|
|||
<StatusRow title="Node Polygon A" cache={serverListStatusCache}>
|
||||
<Text
|
||||
color={
|
||||
nodePolygonAStatus?.status.body.status == "ok"
|
||||
nodePolygonAStatus?.status_code == 200
|
||||
? healthyStatusColor
|
||||
: downStatusColor
|
||||
}
|
||||
>
|
||||
{nodePolygonAStatus?.status.body.status == "ok"
|
||||
{nodePolygonAStatus?.status_code == 200
|
||||
? healthyStatusText
|
||||
: downStatusText}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
<StatusRow title="Current block" cache={serverListStatusCache}>
|
||||
<Text>
|
||||
{nodePolygonAGeth?.status.body.current_block
|
||||
? nodePolygonAGeth.status.body.current_block
|
||||
{nodePolygonAStatus?.response?.current_block
|
||||
? nodePolygonAStatus.response.current_block
|
||||
: 0}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
|
@ -200,49 +181,105 @@ const Status = () => {
|
|||
<StatusRow title="Node Polygon B" cache={serverListStatusCache}>
|
||||
<Text
|
||||
color={
|
||||
nodePolygonBStatus?.status.body.status == "ok"
|
||||
nodePolygonBStatus?.status_code == 200
|
||||
? healthyStatusColor
|
||||
: downStatusColor
|
||||
}
|
||||
>
|
||||
{nodePolygonBStatus?.status.body.status == "ok"
|
||||
{nodePolygonBStatus?.status_code == 200
|
||||
? healthyStatusText
|
||||
: downStatusText}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
<StatusRow title="Current block" cache={serverListStatusCache}>
|
||||
<Text>
|
||||
{nodePolygonBGeth?.status.body.current_block
|
||||
? nodePolygonBGeth.status.body.current_block
|
||||
{nodePolygonBStatus?.response?.current_block
|
||||
? nodePolygonBStatus.response.current_block
|
||||
: 0}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
|
||||
<br />
|
||||
|
||||
<StatusRow title="Database server" cache={dbServerStatusCache}>
|
||||
<StatusRow title="Database server" cache={serverListStatusCache}>
|
||||
<Text
|
||||
color={
|
||||
dbServerStatusCache?.data?.status == "ok"
|
||||
dbServerStatus?.status_code == 200
|
||||
? healthyStatusColor
|
||||
: downStatusColor
|
||||
}
|
||||
>
|
||||
{dbServerStatusCache?.data?.status == "ok"
|
||||
{dbServerStatus?.status_code == 200
|
||||
? healthyStatusText
|
||||
: downStatusText}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
<StatusRow
|
||||
title="Latest block in Database"
|
||||
cache={latestBlockDBStatusCache}
|
||||
>
|
||||
<StatusRow title="Ethereum latest block" cache={serverListStatusCache}>
|
||||
<Text>
|
||||
{latestBlockDBStatusCache?.data?.block_number
|
||||
? latestBlockDBStatusCache.data.block_number
|
||||
{dbServerStatus?.response?.ethereum_block_latest
|
||||
? dbServerStatus.response.ethereum_block_latest
|
||||
: 0}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
<StatusRow title="Polygon latest block" cache={serverListStatusCache}>
|
||||
<Text>
|
||||
{dbServerStatus?.response?.polygon_block_latest
|
||||
? dbServerStatus.response.polygon_block_latest
|
||||
: 0}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
|
||||
<br />
|
||||
|
||||
<StatusRow
|
||||
title="Database replica server"
|
||||
cache={serverListStatusCache}
|
||||
>
|
||||
<Text
|
||||
color={
|
||||
dbReplicaServerStatus?.status_code == 200
|
||||
? healthyStatusColor
|
||||
: downStatusColor
|
||||
}
|
||||
>
|
||||
{dbReplicaServerStatus?.status_code == 200
|
||||
? healthyStatusText
|
||||
: downStatusText}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
<StatusRow title="Ethereum latest block" cache={serverListStatusCache}>
|
||||
<Text>
|
||||
{dbReplicaServerStatus?.response?.ethereum_block_latest
|
||||
? dbReplicaServerStatus.response.ethereum_block_latest
|
||||
: 0}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
<StatusRow title="Polygon latest block" cache={serverListStatusCache}>
|
||||
<Text>
|
||||
{dbReplicaServerStatus?.response?.polygon_block_latest
|
||||
? dbReplicaServerStatus.response.polygon_block_latest
|
||||
: 0}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
|
||||
<br />
|
||||
|
||||
<StatusRow
|
||||
title="Unim Leaderboard server"
|
||||
cache={serverListStatusCache}
|
||||
>
|
||||
<Text
|
||||
color={
|
||||
unimLeaderboardStatus?.status_code == 200
|
||||
? healthyStatusColor
|
||||
: downStatusColor
|
||||
}
|
||||
>
|
||||
{unimLeaderboardStatus?.status_code == 200
|
||||
? healthyStatusText
|
||||
: downStatusText}
|
||||
</Text>
|
||||
</StatusRow>
|
||||
</chakra.span>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
import { AiOutlinePlusCircle } from "react-icons/ai";
|
||||
import OverlayContext from "../src/core/providers/OverlayProvider/context";
|
||||
import { MODAL_TYPES } from "../src/core/providers/OverlayProvider/constants";
|
||||
|
||||
import Scrollable from "../src/components/Scrollable";
|
||||
const Subscriptions = () => {
|
||||
const { subscriptionsCache } = useSubscriptions();
|
||||
const modal = useContext(OverlayContext);
|
||||
|
@ -37,64 +37,66 @@ const Subscriptions = () => {
|
|||
modal.toggleModal({ type: MODAL_TYPES.NEW_SUBSCRIPTON });
|
||||
};
|
||||
return (
|
||||
<Box w="100%" px="7%" pt={2}>
|
||||
{subscriptionsCache.isLoading ? (
|
||||
<Center>
|
||||
<Spinner
|
||||
hidden={false}
|
||||
my={8}
|
||||
size="lg"
|
||||
color="blue.500"
|
||||
thickness="4px"
|
||||
speed="1.5s"
|
||||
/>
|
||||
</Center>
|
||||
) : (
|
||||
<ScaleFade in>
|
||||
<Heading {...headingStyle}> My Subscriptions </Heading>
|
||||
<Flex
|
||||
mt={4}
|
||||
overflow="initial"
|
||||
maxH="unset"
|
||||
height="100%"
|
||||
direction="column"
|
||||
>
|
||||
<Scrollable>
|
||||
<Box w="100%" px="7%" pt={2}>
|
||||
{subscriptionsCache.isLoading ? (
|
||||
<Center>
|
||||
<Spinner
|
||||
hidden={false}
|
||||
my={8}
|
||||
size="lg"
|
||||
color="blue.500"
|
||||
thickness="4px"
|
||||
speed="1.5s"
|
||||
/>
|
||||
</Center>
|
||||
) : (
|
||||
<ScaleFade in>
|
||||
<Heading {...headingStyle}> My Subscriptions </Heading>
|
||||
<Flex
|
||||
h="3rem"
|
||||
w="100%"
|
||||
bgColor="blue.50"
|
||||
borderTopRadius="xl"
|
||||
justifyContent="flex-end"
|
||||
alignItems="center"
|
||||
mt={4}
|
||||
overflow="initial"
|
||||
maxH="unset"
|
||||
height="100%"
|
||||
direction="column"
|
||||
>
|
||||
{subscriptionsCache.data?.is_free_subscription_availible && (
|
||||
<Flex
|
||||
h="3rem"
|
||||
w="100%"
|
||||
bgColor="blue.50"
|
||||
borderTopRadius="xl"
|
||||
justifyContent="flex-end"
|
||||
alignItems="center"
|
||||
>
|
||||
{subscriptionsCache.data?.is_free_subscription_availible && (
|
||||
<Button
|
||||
onClick={() => newSubscriptionClicked(true)}
|
||||
mr={8}
|
||||
colorScheme="green"
|
||||
variant="solid"
|
||||
size="sm"
|
||||
rightIcon={<AiOutlinePlusCircle />}
|
||||
>
|
||||
Add for free
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={() => newSubscriptionClicked(true)}
|
||||
onClick={() => newSubscriptionClicked(false)}
|
||||
mr={8}
|
||||
colorScheme="green"
|
||||
colorScheme="blue"
|
||||
variant="solid"
|
||||
size="sm"
|
||||
rightIcon={<AiOutlinePlusCircle />}
|
||||
>
|
||||
Add for free
|
||||
Add new
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={() => newSubscriptionClicked(false)}
|
||||
mr={8}
|
||||
colorScheme="blue"
|
||||
variant="solid"
|
||||
size="sm"
|
||||
rightIcon={<AiOutlinePlusCircle />}
|
||||
>
|
||||
Add new
|
||||
</Button>
|
||||
</Flex>
|
||||
<SubscriptionsList data={subscriptionsCache.data} />
|
||||
</Flex>
|
||||
<SubscriptionsList data={subscriptionsCache.data} />
|
||||
</Flex>
|
||||
</ScaleFade>
|
||||
)}
|
||||
</Box>
|
||||
</ScaleFade>
|
||||
)}
|
||||
</Box>
|
||||
</Scrollable>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ export NEXT_PUBLIC_MIXPANEL_TOKEN="<YOUR MIXPANEL TOKEN HERE>"
|
|||
export NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="<stripe publishable key>"
|
||||
export NEXT_PUBLIC_BUGOUT_STATUS_URL=https://status.moonstream.to
|
||||
export NEXT_PUBLIC_MOONSTREAM_API_URL=https://api.moonstream.to
|
||||
export NEXT_PUBLIC_MOONSTREAM_DB_URL=https://pg.moonstream.to
|
||||
export NEXT_PUBLIC_SIMIOTICS_AUTH_URL=https://auth.bugout.dev
|
||||
export NEXT_PUBLIC_SIMIOTICS_JOURNALS_URL=https://spire.bugout.dev
|
||||
export NEXT_PUBLIC_FRONTEND_VERSION="<frontend_version_number>"
|
||||
|
|
|
@ -114,6 +114,24 @@ const variantGhost = (props) => {
|
|||
};
|
||||
};
|
||||
|
||||
const variantOrangeAndBlue = () => {
|
||||
return {
|
||||
minW: ["200px", "250px", "250px", "300px", "350px", "400px"],
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
border: "solid transparent",
|
||||
fontWeight: "bold",
|
||||
rounded: ["lg", "xl", "2xl"],
|
||||
shadow: "md",
|
||||
variant: "solid",
|
||||
textColor: "blue.1200",
|
||||
bg: `orange.900`,
|
||||
fontSize: ["lg", "xl", "2xl", "3xl", "4xl", "4xl"],
|
||||
py: [4, 6, 6, 8, 8],
|
||||
px: [4, 4, 4, 8, 8],
|
||||
};
|
||||
};
|
||||
|
||||
const Button = {
|
||||
// 1. We can update the base styles
|
||||
baseStyle: () => ({
|
||||
|
@ -151,6 +169,7 @@ const Button = {
|
|||
ghost: variantGhost,
|
||||
outline: variantOutline,
|
||||
link: variantLink,
|
||||
orangeAndBlue: variantOrangeAndBlue,
|
||||
},
|
||||
};
|
||||
export default Button;
|
||||
|
|
|
@ -9,11 +9,12 @@ import {
|
|||
MenuDivider,
|
||||
IconButton,
|
||||
chakra,
|
||||
Portal,
|
||||
} from "@chakra-ui/react";
|
||||
import { RiAccountCircleLine } from "react-icons/ri";
|
||||
import useLogout from "../core/hooks/useLogout";
|
||||
import UIContext from "../core/providers/UIProvider/context";
|
||||
import { ALL_NAV_PATHES } from "../core/constants";
|
||||
import { SITEMAP } from "../core/constants";
|
||||
|
||||
const AccountIconButton = (props) => {
|
||||
const { logout } = useLogout();
|
||||
|
@ -30,39 +31,49 @@ const AccountIconButton = (props) => {
|
|||
icon={<RiAccountCircleLine m={0} size="26px" />}
|
||||
color="gray.100"
|
||||
/>
|
||||
<MenuList
|
||||
zIndex="dropdown"
|
||||
width={["100vw", "100vw", "18rem", "20rem", "22rem", "24rem"]}
|
||||
borderRadius={0}
|
||||
>
|
||||
<MenuGroup>
|
||||
<RouterLink href="/account/security" passHref>
|
||||
<MenuItem>Security</MenuItem>
|
||||
</RouterLink>
|
||||
<RouterLink href="/account/tokens" passHref>
|
||||
<MenuItem>API tokens</MenuItem>
|
||||
</RouterLink>
|
||||
</MenuGroup>
|
||||
<MenuDivider />
|
||||
{ui.isMobileView &&
|
||||
ALL_NAV_PATHES.map((pathToLink, idx) => {
|
||||
return (
|
||||
<MenuItem key={`AccountIconButton-All_nav_pathes-${idx}`}>
|
||||
<RouterLink href={pathToLink.path}>
|
||||
{pathToLink.title}
|
||||
</RouterLink>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
logout();
|
||||
}}
|
||||
<Portal>
|
||||
<MenuList
|
||||
zIndex="dropdown"
|
||||
width={["100vw", "100vw", "18rem", "20rem", "22rem", "24rem"]}
|
||||
borderRadius={0}
|
||||
>
|
||||
Logout
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
<MenuGroup>
|
||||
<RouterLink href="/account/security" passHref>
|
||||
<MenuItem>Security</MenuItem>
|
||||
</RouterLink>
|
||||
<RouterLink href="/account/tokens" passHref>
|
||||
<MenuItem>API tokens</MenuItem>
|
||||
</RouterLink>
|
||||
</MenuGroup>
|
||||
<MenuDivider />
|
||||
{ui.isMobileView &&
|
||||
SITEMAP.map((item, idx) => {
|
||||
if (item.children) {
|
||||
return (
|
||||
<MenuGroup key={`AccountIconButton-MenuGroup-${idx}`}>
|
||||
{item.children.map((child, idx) => {
|
||||
return (
|
||||
<MenuItem key={`AccountIconButton-SITEMAP-${idx}`}>
|
||||
<RouterLink href={child.path}>
|
||||
{child.title}
|
||||
</RouterLink>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</MenuGroup>
|
||||
);
|
||||
}
|
||||
})}
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
logout();
|
||||
}}
|
||||
>
|
||||
Logout
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Portal>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState, useContext, useEffect } from "react";
|
||||
import RouterLink from "next/link";
|
||||
import {
|
||||
Flex,
|
||||
Image,
|
||||
|
@ -15,12 +16,19 @@ import {
|
|||
useBreakpointValue,
|
||||
Spacer,
|
||||
ButtonGroup,
|
||||
Button,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItem,
|
||||
Portal,
|
||||
} from "@chakra-ui/react";
|
||||
import {
|
||||
HamburgerIcon,
|
||||
QuestionOutlineIcon,
|
||||
ArrowLeftIcon,
|
||||
ArrowRightIcon,
|
||||
ChevronDownIcon,
|
||||
} from "@chakra-ui/icons";
|
||||
import useRouter from "../core/hooks/useRouter";
|
||||
import UIContext from "../core/providers/UIProvider/context";
|
||||
|
@ -29,7 +37,7 @@ import RouteButton from "./RouteButton";
|
|||
import AddNewIconButton from "./AddNewIconButton";
|
||||
import {
|
||||
USER_NAV_PATHES,
|
||||
ALL_NAV_PATHES,
|
||||
SITEMAP,
|
||||
WHITE_LOGO_W_TEXT_URL,
|
||||
} from "../core/constants";
|
||||
|
||||
|
@ -98,21 +106,53 @@ const AppNavbar = () => {
|
|||
<>
|
||||
{!ui.isMobileView && (
|
||||
<>
|
||||
<Flex width="100%" px={2}>
|
||||
<Flex px={2}>
|
||||
<Spacer />
|
||||
<Flex placeSelf="flex-end">
|
||||
<ButtonGroup spacing={4} colorScheme="orange">
|
||||
{ALL_NAV_PATHES.map((item, idx) => (
|
||||
<RouteButton
|
||||
key={`${idx}-${item.title}-landing-all-links`}
|
||||
variant="link"
|
||||
href={item.path}
|
||||
color="white"
|
||||
isActive={!!(router.nextRouter.pathname === item.path)}
|
||||
>
|
||||
{item.title}
|
||||
</RouteButton>
|
||||
))}
|
||||
<ButtonGroup variant="link" spacing={4} colorScheme="orange">
|
||||
{SITEMAP.map((item, idx) => {
|
||||
if (!item.children) {
|
||||
return (
|
||||
<RouteButton
|
||||
key={`${idx}-${item.title}-landing-all-links`}
|
||||
variant="link"
|
||||
href={item.path}
|
||||
color="white"
|
||||
isActive={!!(router.pathname === item.path)}
|
||||
>
|
||||
{item.title}
|
||||
</RouteButton>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Menu key={`menu-${idx}`}>
|
||||
<MenuButton
|
||||
key={`menu-button-${idx}`}
|
||||
as={Button}
|
||||
rightIcon={<ChevronDownIcon />}
|
||||
>
|
||||
{item.title}
|
||||
</MenuButton>
|
||||
<Portal>
|
||||
<MenuList zIndex={100}>
|
||||
{item.children.map((child, idx) => (
|
||||
<RouterLink
|
||||
shallow={true}
|
||||
key={`${idx}-${item.title}-menu-links`}
|
||||
href={child.path}
|
||||
passHref
|
||||
>
|
||||
<MenuItem key={`menu-${idx}`} as={"a"} m={0}>
|
||||
{child.title}
|
||||
</MenuItem>
|
||||
</RouterLink>
|
||||
))}
|
||||
</MenuList>
|
||||
</Portal>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
})}
|
||||
{USER_NAV_PATHES.map((item, idx) => {
|
||||
return (
|
||||
<RouteButton
|
||||
|
|
|
@ -12,7 +12,8 @@ import { BiCopy } from "react-icons/bi";
|
|||
|
||||
const CopyButton = (props) => {
|
||||
const children = props.children ? props.children : "";
|
||||
const copyString = props.prefix ? props.prefix + children : children;
|
||||
const copyString =
|
||||
props.copyString ?? (props.prefix ? props.prefix + children : children);
|
||||
|
||||
const { onCopy } = useClipboard(copyString);
|
||||
|
||||
|
@ -31,7 +32,7 @@ const CopyButton = (props) => {
|
|||
icon={<BiCopy />}
|
||||
colorScheme="orange"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
size={props.size ?? "sm"}
|
||||
/>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
|
|
|
@ -12,11 +12,7 @@ import {
|
|||
chakra,
|
||||
} from "@chakra-ui/react";
|
||||
import RouterLink from "next/link";
|
||||
import {
|
||||
WHITE_LOGO_W_TEXT_URL,
|
||||
ALL_NAV_PATHES,
|
||||
FOOTER_COLUMNS,
|
||||
} from "../core/constants";
|
||||
import { WHITE_LOGO_W_TEXT_URL, SITEMAP } from "../core/constants";
|
||||
import { FaGithub, FaTwitter, FaDiscord } from "react-icons/fa";
|
||||
import moment from "moment";
|
||||
|
||||
|
@ -69,9 +65,9 @@ const Footer = () => (
|
|||
bg={useColorModeValue("blue.900", "gray.900")}
|
||||
color={useColorModeValue("gray.700", "gray.200")}
|
||||
>
|
||||
<Container as={Stack} maxW={"6xl"} py={10}>
|
||||
<Container as={Stack} maxW={"8xl"} py={10}>
|
||||
<SimpleGrid
|
||||
templateColumns={{ sm: "1fr 1fr", md: "2fr 1fr 1fr 2fr" }}
|
||||
templateColumns={{ sm: "1fr 1fr", md: "2fr 1fr 1fr 1fr 1fr" }}
|
||||
spacing={8}
|
||||
>
|
||||
<Stack spacing={6}>
|
||||
|
@ -104,24 +100,35 @@ const Footer = () => (
|
|||
>
|
||||
<FaGithub />
|
||||
</SocialButton>
|
||||
<SocialButton
|
||||
label={"Discord"}
|
||||
href={"https://discord.gg/K56VNUQGvA"}
|
||||
>
|
||||
<SocialButton label={"Discord"} href={"/discordleed"}>
|
||||
<FaDiscord />
|
||||
</SocialButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
{Object.values(FOOTER_COLUMNS).map((columnEnum, colIndex) => {
|
||||
{Object.values(SITEMAP).map((category, colIndex) => {
|
||||
return (
|
||||
<Stack align={"flex-start"} key={`footer-list-column-${colIndex}`}>
|
||||
{ALL_NAV_PATHES.filter(
|
||||
(navPath) => navPath.footerCategory === columnEnum
|
||||
<>
|
||||
<ListHeader>{category.title}</ListHeader>
|
||||
{category.children.map((linkItem, linkItemIndex) => {
|
||||
return (
|
||||
<RouterLink
|
||||
passHref
|
||||
href={linkItem.path}
|
||||
key={`footer-list-link-item-${linkItemIndex}-col-${colIndex}`}
|
||||
>
|
||||
<Link {...LINKS_SIZES}>{linkItem.title}</Link>
|
||||
</RouterLink>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
{/* {SITEMAP.filter(
|
||||
(navPath) => navPath.SiteMapCategory === columnEnum
|
||||
).length > 0 && (
|
||||
<>
|
||||
<ListHeader>{columnEnum}</ListHeader>
|
||||
{ALL_NAV_PATHES.filter(
|
||||
(navPath) => navPath.footerCategory === columnEnum
|
||||
(navPath) => navPath.SiteMapCategory === columnEnum
|
||||
).map((linkItem, linkItemIndex) => {
|
||||
return (
|
||||
<RouterLink
|
||||
|
@ -134,7 +141,7 @@ const Footer = () => (
|
|||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
)} */}
|
||||
</Stack>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -8,13 +8,18 @@ import {
|
|||
Link,
|
||||
IconButton,
|
||||
Flex,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItem,
|
||||
Portal,
|
||||
} from "@chakra-ui/react";
|
||||
import { HamburgerIcon } from "@chakra-ui/icons";
|
||||
import { ChevronDownIcon, HamburgerIcon } from "@chakra-ui/icons";
|
||||
import useModals from "../core/hooks/useModals";
|
||||
import UIContext from "../core/providers/UIProvider/context";
|
||||
import ChakraAccountIconButton from "./AccountIconButton";
|
||||
import RouteButton from "./RouteButton";
|
||||
import { ALL_NAV_PATHES, WHITE_LOGO_W_TEXT_URL } from "../core/constants";
|
||||
import { SITEMAP, WHITE_LOGO_W_TEXT_URL } from "../core/constants";
|
||||
import router from "next/router";
|
||||
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
|
||||
|
||||
|
@ -37,7 +42,7 @@ const LandingNavbar = () => {
|
|||
<Flex
|
||||
pl={ui.isMobileView ? 2 : 8}
|
||||
justifySelf="flex-start"
|
||||
h="100%"
|
||||
h="48px"
|
||||
py={1}
|
||||
flexBasis="200px"
|
||||
flexGrow={1}
|
||||
|
@ -47,7 +52,7 @@ const LandingNavbar = () => {
|
|||
<Link
|
||||
as={Image}
|
||||
w="auto"
|
||||
h="full"
|
||||
h="100%"
|
||||
justifyContent="left"
|
||||
src={WHITE_LOGO_W_TEXT_URL}
|
||||
alt="Moonstream logo"
|
||||
|
@ -59,21 +64,51 @@ const LandingNavbar = () => {
|
|||
<>
|
||||
<Spacer />
|
||||
<ButtonGroup variant="link" colorScheme="orange" spacing={4} pr={16}>
|
||||
{ALL_NAV_PATHES.map((item, idx) => (
|
||||
<RouteButton
|
||||
key={`${idx}-${item.title}-landing-all-links`}
|
||||
variant="link"
|
||||
href={item.path}
|
||||
color="white"
|
||||
isActive={!!(router.pathname === item.path)}
|
||||
>
|
||||
{item.title}
|
||||
</RouteButton>
|
||||
))}
|
||||
{SITEMAP.map((item, idx) => {
|
||||
return (
|
||||
<React.Fragment key={`Fragment-${idx}`}>
|
||||
{!item.children && (
|
||||
<RouteButton
|
||||
key={`${idx}-${item.title}-landing-all-links`}
|
||||
variant="link"
|
||||
href={item.path}
|
||||
color="white"
|
||||
isActive={!!(router.pathname === item.path)}
|
||||
>
|
||||
{item.title}
|
||||
</RouteButton>
|
||||
)}
|
||||
{item.children && (
|
||||
<Menu>
|
||||
<MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
|
||||
{item.title}
|
||||
</MenuButton>
|
||||
<Portal>
|
||||
<MenuList zIndex={100}>
|
||||
{item.children.map((child, idx) => (
|
||||
<RouterLink
|
||||
shallow={true}
|
||||
key={`${idx}-${item.title}-menu-links`}
|
||||
href={child.path}
|
||||
passHref
|
||||
>
|
||||
<MenuItem key={`menu-${idx}`} as={"a"} m={0}>
|
||||
{child.title}
|
||||
</MenuItem>
|
||||
</RouterLink>
|
||||
))}
|
||||
</MenuList>
|
||||
</Portal>
|
||||
</Menu>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
|
||||
{ui.isLoggedIn && (
|
||||
<RouterLink href="/welcome" passHref>
|
||||
<Button
|
||||
alignSelf={"center"}
|
||||
as={Link}
|
||||
colorScheme="orange"
|
||||
variant="outline"
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { React } from "react";
|
||||
import { chakra, Box, Text } from "@chakra-ui/react";
|
||||
|
||||
const _MilestoneBox = ({ headingText }) => {
|
||||
return (
|
||||
<Box
|
||||
minW={["150px", "180px", "300px", "300px", "400px", "500px"]}
|
||||
py={5}
|
||||
px={3}
|
||||
>
|
||||
<Text
|
||||
fontSize={["md", "2xl", "3xl", "4xl", "4xl", "4xl"]}
|
||||
ml={5}
|
||||
color="orange.500"
|
||||
fontWeight="bold"
|
||||
textAlign="center"
|
||||
>
|
||||
{headingText}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const MilestoneBox = chakra(_MilestoneBox);
|
||||
|
||||
export default MilestoneBox;
|
|
@ -2,7 +2,7 @@ import React, { Suspense, useContext } from "react";
|
|||
import { Flex } from "@chakra-ui/react";
|
||||
import UIContext from "../core/providers/UIProvider/context";
|
||||
|
||||
const LandingNavbar = React.lazy(() => import("./LandingNavbar"));
|
||||
import LandingNavbar from "./LandingNavbar";
|
||||
const AppNavbar = React.lazy(() => import("./AppNavbar"));
|
||||
|
||||
const Navbar = () => {
|
||||
|
@ -11,6 +11,7 @@ const Navbar = () => {
|
|||
return (
|
||||
<Flex
|
||||
boxShadow={["sm", "md"]}
|
||||
zIndex={1}
|
||||
alignItems="center"
|
||||
id="Navbar"
|
||||
minH="3rem"
|
||||
|
@ -19,9 +20,12 @@ const Navbar = () => {
|
|||
direction="row"
|
||||
w="100%"
|
||||
overflow="hidden"
|
||||
position={"fixed"}
|
||||
transition={"0.3s"}
|
||||
top={"0"}
|
||||
>
|
||||
{(!isAppView || !isLoggedIn) && <LandingNavbar />}
|
||||
<Suspense fallback={""}>
|
||||
{(!isAppView || !isLoggedIn) && <LandingNavbar />}
|
||||
{isAppView && isLoggedIn && <AppNavbar />}
|
||||
</Suspense>
|
||||
</Flex>
|
||||
|
|
|
@ -2,14 +2,14 @@ import { Flex, Box } from "@chakra-ui/react";
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useRouter } from "../core/hooks";
|
||||
import mixpanel from "mixpanel-browser";
|
||||
import { useXarrow, Xwrapper } from "react-xarrows";
|
||||
const Scrollable = (props) => {
|
||||
const scrollerRef = useRef();
|
||||
const router = useRouter();
|
||||
const [path, setPath] = useState();
|
||||
const updateXarrow = useXarrow();
|
||||
|
||||
const [scrollDepth, setScrollDepth] = useState(0);
|
||||
const [y, setY] = useState(0);
|
||||
const [dir, setDir] = useState(0);
|
||||
|
||||
const getScrollPrecent = ({ currentTarget }) => {
|
||||
const scroll_level =
|
||||
|
@ -19,8 +19,20 @@ const Scrollable = (props) => {
|
|||
};
|
||||
|
||||
const handleScroll = (e) => {
|
||||
updateXarrow();
|
||||
const currentScroll = Math.ceil(getScrollPrecent(e) / 10);
|
||||
if (currentScroll) {
|
||||
if (currentScroll != y) {
|
||||
setDir(y - currentScroll);
|
||||
setY(currentScroll);
|
||||
}
|
||||
}
|
||||
|
||||
if (dir === -1) {
|
||||
document.getElementById("Navbar").style.top = "-48px";
|
||||
} else {
|
||||
document.getElementById("Navbar").style.top = "-0";
|
||||
}
|
||||
setY(currentScroll);
|
||||
if (currentScroll > scrollDepth) {
|
||||
setScrollDepth(currentScroll);
|
||||
mixpanel?.get_distinct_id() &&
|
||||
|
@ -51,17 +63,15 @@ const Scrollable = (props) => {
|
|||
overflowY="hidden"
|
||||
maxH="100%"
|
||||
>
|
||||
<Xwrapper>
|
||||
<Box
|
||||
className="Scrollable"
|
||||
direction="column"
|
||||
ref={scrollerRef}
|
||||
overflowY="scroll"
|
||||
onScroll={(e) => handleScroll(e)}
|
||||
>
|
||||
{props.children}
|
||||
</Box>
|
||||
</Xwrapper>
|
||||
<Box
|
||||
className="Scrollable"
|
||||
direction="column"
|
||||
ref={scrollerRef}
|
||||
overflowY="scroll"
|
||||
onScroll={(e) => handleScroll(e)}
|
||||
>
|
||||
{props.children}
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
LockIcon,
|
||||
} from "@chakra-ui/icons";
|
||||
import { MdSettings, MdDashboard, MdTimeline } from "react-icons/md";
|
||||
import { WHITE_LOGO_W_TEXT_URL, ALL_NAV_PATHES } from "../core/constants";
|
||||
import { WHITE_LOGO_W_TEXT_URL, SITEMAP } from "../core/constants";
|
||||
import useDashboard from "../core/hooks/useDashboard";
|
||||
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
|
||||
import OverlayContext from "../core/providers/OverlayProvider/context";
|
||||
|
@ -102,14 +102,27 @@ const Sidebar = () => {
|
|||
Login
|
||||
</MenuItem>
|
||||
{ui.isMobileView &&
|
||||
ALL_NAV_PATHES.map((pathToLink, linkItemIndex) => {
|
||||
return (
|
||||
<MenuItem key={`mobile-all-nav-path-item-${linkItemIndex}`}>
|
||||
<RouterLink href={pathToLink.path}>
|
||||
{pathToLink.title}
|
||||
</RouterLink>
|
||||
</MenuItem>
|
||||
);
|
||||
SITEMAP.map((item, idx) => {
|
||||
if (item.children) {
|
||||
return (
|
||||
<React.Fragment key={`Fragment-${idx}`}>
|
||||
{item.children.map((child, idx) => {
|
||||
return (
|
||||
<MenuItem
|
||||
key={`MenuItem-SITEMAP-${idx}`}
|
||||
onClick={() => {
|
||||
ui.setSidebarToggled(false);
|
||||
}}
|
||||
>
|
||||
<RouterLink href={child.path}>
|
||||
{child.title}
|
||||
</RouterLink>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
|
@ -134,6 +147,9 @@ const Sidebar = () => {
|
|||
<MenuItem
|
||||
icon={<MdDashboard />}
|
||||
key={`dashboard-link-${idx}`}
|
||||
onClick={() => {
|
||||
ui.setSidebarToggled(false);
|
||||
}}
|
||||
>
|
||||
<RouterLink href={`/dashboard/${dashboard?.id}`}>
|
||||
{dashboard.resource_data.name}
|
||||
|
@ -148,11 +164,12 @@ const Sidebar = () => {
|
|||
variant="solid"
|
||||
colorScheme="orange"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
onClick={() => {
|
||||
overlay.toggleModal({
|
||||
type: MODAL_TYPES.NEW_DASHBOARD_FLOW,
|
||||
})
|
||||
}
|
||||
});
|
||||
ui.setSidebarToggled(false);
|
||||
}}
|
||||
// w="100%"
|
||||
// borderRadius={0}
|
||||
>
|
||||
|
@ -175,13 +192,28 @@ const Sidebar = () => {
|
|||
<Divider color="gray.300" w="100%" />
|
||||
{ui.isLoggedIn && (
|
||||
<Menu iconShape="square">
|
||||
<MenuItem icon={<MdSettings />}>
|
||||
<MenuItem
|
||||
icon={<MdSettings />}
|
||||
onClick={() => {
|
||||
ui.setSidebarToggled(false);
|
||||
}}
|
||||
>
|
||||
<RouterLink href="/subscriptions">Subscriptions </RouterLink>
|
||||
</MenuItem>
|
||||
<MenuItem icon={<MdTimeline />}>
|
||||
<MenuItem
|
||||
icon={<MdTimeline />}
|
||||
onClick={() => {
|
||||
ui.setSidebarToggled(false);
|
||||
}}
|
||||
>
|
||||
<RouterLink href="/stream">Stream</RouterLink>
|
||||
</MenuItem>
|
||||
<MenuItem icon={<LockIcon />}>
|
||||
<MenuItem
|
||||
icon={<LockIcon />}
|
||||
onClick={() => {
|
||||
ui.setSidebarToggled(false);
|
||||
}}
|
||||
>
|
||||
<RouterLink href="/account/tokens">API Tokens</RouterLink>
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
|
|
|
@ -40,6 +40,7 @@ const SignUp = ({ toggleModal }) => {
|
|||
colorScheme="blue"
|
||||
placeholder="Your username here"
|
||||
name="username"
|
||||
autoComplete="username"
|
||||
ref={register({ required: "Username is required!" })}
|
||||
/>
|
||||
<InputRightElement>
|
||||
|
@ -57,6 +58,7 @@ const SignUp = ({ toggleModal }) => {
|
|||
colorScheme="blue"
|
||||
placeholder="Your email here"
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
ref={register({ required: "Email is required!" })}
|
||||
/>
|
||||
<InputRightElement>
|
||||
|
@ -71,6 +73,7 @@ const SignUp = ({ toggleModal }) => {
|
|||
<PasswordInput
|
||||
placeholder="Add password"
|
||||
name="password"
|
||||
autoComplete="new-password"
|
||||
ref={register({ required: "Password is required!" })}
|
||||
/>
|
||||
<FormErrorMessage color="red.400" pl="1">
|
||||
|
|
|
@ -12,11 +12,15 @@ import {
|
|||
Button,
|
||||
useBreakpointValue,
|
||||
useToken,
|
||||
chakra,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext } from "react";
|
||||
import UIContext from "../core/providers/UIProvider/context";
|
||||
import { FaDiscord, FaGithubSquare } from "react-icons/fa";
|
||||
import RouteButton from "../components/RouteButton";
|
||||
import mixpanel from "mixpanel-browser";
|
||||
import MIXPANEL_EVENTS from "../core/providers/AnalyticsProvider/constants";
|
||||
import { useRouter } from "../core/hooks";
|
||||
|
||||
const Feature = ({ text, icon, iconBg, bullets }) => {
|
||||
return (
|
||||
|
@ -47,7 +51,7 @@ const Feature = ({ text, icon, iconBg, bullets }) => {
|
|||
text={bullet.text}
|
||||
{...bullet}
|
||||
icon={
|
||||
<Icon as={bullet.icon} color={bullet.color} w={5} h={5} />
|
||||
<Icon as={bullet.icon} color={bullet.color} w={16} h={16} />
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
@ -58,7 +62,7 @@ const Feature = ({ text, icon, iconBg, bullets }) => {
|
|||
);
|
||||
};
|
||||
|
||||
const SplitWithImage = ({
|
||||
const _SplitWithImage = ({
|
||||
badge,
|
||||
title,
|
||||
body,
|
||||
|
@ -71,7 +75,10 @@ const SplitWithImage = ({
|
|||
socialButton,
|
||||
imgBoxShadow,
|
||||
py,
|
||||
...props
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
|
||||
var buttonSize = useBreakpointValue({
|
||||
base: { single: "sm", double: "xs" },
|
||||
sm: { single: "md", double: "sm" },
|
||||
|
@ -116,6 +123,7 @@ const SplitWithImage = ({
|
|||
py={py}
|
||||
className={`fade-in-section ${isVisible ? "is-visible" : ""}`}
|
||||
ref={domRef}
|
||||
{...props}
|
||||
>
|
||||
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={[0, 0, 10, null, 10]}>
|
||||
{mirror && !ui.isMobileView && (
|
||||
|
@ -166,7 +174,7 @@ const SplitWithImage = ({
|
|||
<Feature
|
||||
key={`splitWImageBullet-${idx}-${title}`}
|
||||
icon={
|
||||
<Icon as={bullet.icon} color={bullet.color} w={5} h={5} />
|
||||
<Icon as={bullet.icon} color={bullet.color} w={16} h={16} />
|
||||
}
|
||||
iconBg={bullet.bgColor}
|
||||
text={bullet.text}
|
||||
|
@ -186,6 +194,16 @@ const SplitWithImage = ({
|
|||
w={["100%", "100%", "fit-content", null]}
|
||||
maxW={["250px", null, "fit-content"]}
|
||||
href={socialButton.url}
|
||||
onClick={() => {
|
||||
if (mixpanel.get_distinct_id()) {
|
||||
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
|
||||
full_url: router.nextRouter.asPath,
|
||||
buttonName: `${socialButton.title}`,
|
||||
page: `splitWImage`,
|
||||
section: `${badge}`,
|
||||
});
|
||||
}
|
||||
}}
|
||||
mt={[0, 0, null, 16]}
|
||||
size={socialButton ? buttonSize.double : buttonSize.single}
|
||||
variant="outline"
|
||||
|
@ -206,7 +224,18 @@ const SplitWithImage = ({
|
|||
variant="outline"
|
||||
mt={[0, 0, null, 16]}
|
||||
size={socialButton ? buttonSize.double : buttonSize.single}
|
||||
onClick={cta.onClick}
|
||||
onClick={() => {
|
||||
if (mixpanel.get_distinct_id()) {
|
||||
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
|
||||
full_url: router.nextRouter.asPath,
|
||||
buttonName: `${cta.label}`,
|
||||
page: `splitWImage`,
|
||||
section: `${badge}`,
|
||||
});
|
||||
}
|
||||
|
||||
cta.onClick();
|
||||
}}
|
||||
>
|
||||
{cta.label}
|
||||
</Button>
|
||||
|
@ -230,5 +259,6 @@ const SplitWithImage = ({
|
|||
</Container>
|
||||
);
|
||||
};
|
||||
const SplitWithImage = chakra(_SplitWithImage);
|
||||
|
||||
export default SplitWithImage;
|
||||
|
|
|
@ -0,0 +1,361 @@
|
|||
import React, { useContext } from "react";
|
||||
import { IconButton } from "@chakra-ui/react";
|
||||
import {
|
||||
Td,
|
||||
Tr,
|
||||
Tooltip,
|
||||
Editable,
|
||||
EditableInput,
|
||||
EditablePreview,
|
||||
Image,
|
||||
Button,
|
||||
AccordionItem,
|
||||
AccordionButton,
|
||||
AccordionPanel,
|
||||
AccordionIcon,
|
||||
Flex,
|
||||
Text,
|
||||
Spacer,
|
||||
Stack,
|
||||
} from "@chakra-ui/react";
|
||||
import { CheckIcon, DeleteIcon, EditIcon } from "@chakra-ui/icons";
|
||||
import moment from "moment";
|
||||
import CopyButton from "./CopyButton";
|
||||
import { useSubscriptions } from "../core/hooks";
|
||||
import ConfirmationRequest from "./ConfirmationRequest";
|
||||
import ColorSelector from "./ColorSelector";
|
||||
import OverlayContext from "../core/providers/OverlayProvider/context";
|
||||
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
|
||||
|
||||
const mapper = {
|
||||
"tag:erc721": "NFTs",
|
||||
"input:address": "Address",
|
||||
};
|
||||
|
||||
const SubscriptionCard = ({ subscription, isDesktopView, iconLink }) => {
|
||||
const overlay = useContext(OverlayContext);
|
||||
const { updateSubscription, deleteSubscription, subscriptionTypeNames } =
|
||||
useSubscriptions();
|
||||
const [_isLoading, _setIsLoading] = React.useState(
|
||||
updateSubscription.isLoading
|
||||
);
|
||||
|
||||
const updateCallback = ({ id, label, color }) => {
|
||||
const data = { id: id };
|
||||
label && (data.label = label);
|
||||
color && (data.color = color);
|
||||
updateSubscription.mutate(data);
|
||||
};
|
||||
|
||||
const cellProps = {
|
||||
px: ["16px", "8px", "16px"],
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log("update subscription ue");
|
||||
if (updateSubscription.isLoading) _setIsLoading(true);
|
||||
else _setIsLoading(false);
|
||||
}, [updateSubscription.isLoading]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isDesktopView && (
|
||||
<AccordionItem
|
||||
bgColor="blue.50"
|
||||
borderBottomColor="blue.500"
|
||||
key={`token-row-${subscription.id}`}
|
||||
>
|
||||
<h2>
|
||||
<AccordionButton>
|
||||
<Stack
|
||||
direction="row"
|
||||
textAlign="center"
|
||||
alignItems="center"
|
||||
w="100%"
|
||||
>
|
||||
<Tooltip
|
||||
label={`${
|
||||
subscriptionTypeNames[subscription.subscription_type_id]
|
||||
}`}
|
||||
fontSize="md"
|
||||
>
|
||||
<Image
|
||||
h={["32px", "16px", "32px", null]}
|
||||
src={iconLink}
|
||||
alt="pool icon"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Text>{subscription.label}</Text>
|
||||
|
||||
{/* <Input
|
||||
w="100%"
|
||||
colorScheme="blue"
|
||||
placeholder="enter note here"
|
||||
// isDisabled={!isEditing}
|
||||
isReadOnly={isEditing}
|
||||
// isPreviewFocusable={false}
|
||||
defaultValue={subscription.label}
|
||||
onSubmit={handleSubmit}
|
||||
value={inputState}
|
||||
onChange={(e) => {
|
||||
setInputState(e.target.value);
|
||||
}}
|
||||
// onClick={() => { setIsEditing(true) }}
|
||||
ref={inputRef}
|
||||
variant="outline"
|
||||
></Input> */}
|
||||
</Stack>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
</h2>
|
||||
<AccordionPanel pb={4} bgColor="blue.100" boxShadow="md">
|
||||
<Stack>
|
||||
<Stack fontSize="sm" h="min-content" pr={0}>
|
||||
<Text placeSelf="flex-start">Address:</Text>
|
||||
{/* <Spacer /> */}
|
||||
|
||||
{subscription.address?.startsWith("tag") ? (
|
||||
<CopyButton size="xs" copyString={subscription.address}>
|
||||
{mapper[subscription.address]}
|
||||
</CopyButton>
|
||||
) : (
|
||||
<Flex
|
||||
alignItems="center"
|
||||
size="xs"
|
||||
position="relative"
|
||||
maxWidth="200px"
|
||||
// w="100%"
|
||||
flexGrow={1}
|
||||
>
|
||||
<CopyButton copyString={subscription.address}></CopyButton>
|
||||
<Text
|
||||
isTruncated
|
||||
dataFileType={subscription.address.slice(-3)}
|
||||
whiteSpace="nowrap"
|
||||
textOverflow="ellipsis"
|
||||
overflow="hidden"
|
||||
_after={{
|
||||
content: "attr(datafiletype)",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: "100%",
|
||||
pt: "9px",
|
||||
marginLeft: "-2px",
|
||||
}}
|
||||
>
|
||||
{subscription.address}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</Stack>
|
||||
<Flex
|
||||
fontSize="sm"
|
||||
placeContent="center"
|
||||
h="min-content"
|
||||
alignItems="center"
|
||||
pr={0}
|
||||
>
|
||||
<Text>Abi:</Text>
|
||||
<Spacer />
|
||||
{subscription.abi ? (
|
||||
<CheckIcon />
|
||||
) : (
|
||||
// <Button
|
||||
// colorScheme="orange"
|
||||
// size="xs"
|
||||
// py={2}
|
||||
// disabled={!subscription.address}
|
||||
// onClick={() =>
|
||||
// overlay.toggleModal({
|
||||
// type: MODAL_TYPES.UPLOAD_ABI,
|
||||
// props: { id: subscription.id },
|
||||
// })
|
||||
// }
|
||||
// >
|
||||
// Upload
|
||||
// </Button>
|
||||
<Text>Not Available</Text>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex fontSize="sm" placeContent="center" h="min-content" pr={0}>
|
||||
<Spacer />
|
||||
|
||||
<Flex
|
||||
alignItems="center"
|
||||
direction="column"
|
||||
spacing={0}
|
||||
w="100%"
|
||||
>
|
||||
{/* {isEditing && (
|
||||
<ButtonGroup justifyContent="center" size="sm">
|
||||
<IconButton icon={<CheckIcon />} onClick={handleSubmit} />
|
||||
<IconButton
|
||||
icon={<CloseIcon />}
|
||||
onClick={() => setIsEditing(false)}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
)} */}
|
||||
<Button
|
||||
m={0}
|
||||
borderRadius={0}
|
||||
borderTopRadius="md"
|
||||
w="100%"
|
||||
colorScheme="blue"
|
||||
size="xs"
|
||||
py={2}
|
||||
disabled={!subscription.address}
|
||||
onClick={() => {
|
||||
console.log("rename button clicked");
|
||||
overlay.toggleModal({
|
||||
type: MODAL_TYPES.MOBILE_INPUT_FIELD,
|
||||
props: {
|
||||
title: "New name",
|
||||
initialValue: subscription.label,
|
||||
cancelText: "Cancel",
|
||||
submitText: "Rename",
|
||||
id: subscription.id,
|
||||
},
|
||||
_key: `rename-subscription-${subscription.id}-${_isLoading}`,
|
||||
});
|
||||
// setIsEditing(true);
|
||||
}}
|
||||
leftIcon={<EditIcon />}
|
||||
>
|
||||
Rename
|
||||
</Button>
|
||||
<Button
|
||||
m={0}
|
||||
borderRadius={0}
|
||||
w="100%"
|
||||
colorScheme="orange"
|
||||
size="xs"
|
||||
py={2}
|
||||
disabled={!subscription.address}
|
||||
onClick={() =>
|
||||
overlay.toggleModal({
|
||||
type: MODAL_TYPES.UPLOAD_ABI,
|
||||
props: { id: subscription.id },
|
||||
})
|
||||
}
|
||||
leftIcon={<DeleteIcon />}
|
||||
>
|
||||
Upload Abi
|
||||
</Button>
|
||||
<ConfirmationRequest
|
||||
bodyMessage={"please confirm"}
|
||||
header={"Delete subscription"}
|
||||
onConfirm={() => deleteSubscription.mutate(subscription.id)}
|
||||
>
|
||||
<Button
|
||||
m={0}
|
||||
borderRadius={0}
|
||||
borderBottomRadius="md"
|
||||
w="100%"
|
||||
colorScheme="red"
|
||||
size="xs"
|
||||
py={2}
|
||||
disabled={!subscription.address}
|
||||
leftIcon={<DeleteIcon />}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</ConfirmationRequest>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
)}
|
||||
{isDesktopView && (
|
||||
<Tr key={`token-row-${subscription.id}`}>
|
||||
<Td {...cellProps}>
|
||||
<Tooltip
|
||||
label={`${
|
||||
subscriptionTypeNames[subscription.subscription_type_id]
|
||||
}`}
|
||||
fontSize="md"
|
||||
>
|
||||
<Image
|
||||
h={["32px", "16px", "32px", null]}
|
||||
src={iconLink}
|
||||
alt="pool icon"
|
||||
/>
|
||||
</Tooltip>
|
||||
</Td>
|
||||
<Td py={0} {...cellProps} wordBreak="break-word">
|
||||
<Editable
|
||||
colorScheme="blue"
|
||||
placeholder="enter note here"
|
||||
defaultValue={subscription.label}
|
||||
onSubmit={(nextValue) =>
|
||||
updateCallback({
|
||||
id: subscription.id,
|
||||
label: nextValue,
|
||||
})
|
||||
}
|
||||
>
|
||||
<EditablePreview maxW="40rem" _placeholder={{ color: "black" }} />
|
||||
<EditableInput maxW="40rem" />
|
||||
</Editable>
|
||||
</Td>
|
||||
<Td mr={4} p={0} wordBreak="break-word" {...cellProps}>
|
||||
{subscription.address?.startsWith("tag") ? (
|
||||
<CopyButton>{mapper[subscription.address]}</CopyButton>
|
||||
) : (
|
||||
<CopyButton>{subscription.address}</CopyButton>
|
||||
)}
|
||||
</Td>
|
||||
<Td mr={4} p={0} {...cellProps}>
|
||||
{subscription.abi ? (
|
||||
<CheckIcon />
|
||||
) : (
|
||||
<Button
|
||||
colorScheme="orange"
|
||||
size="xs"
|
||||
py={2}
|
||||
disabled={!subscription.address}
|
||||
onClick={() =>
|
||||
overlay.toggleModal({
|
||||
type: MODAL_TYPES.UPLOAD_ABI,
|
||||
props: { id: subscription.id },
|
||||
})
|
||||
}
|
||||
>
|
||||
Upload
|
||||
</Button>
|
||||
)}
|
||||
</Td>
|
||||
<Td {...cellProps}>
|
||||
<ColorSelector
|
||||
// subscriptionId={subscription.id}
|
||||
initialColor={subscription.color}
|
||||
callback={(color) =>
|
||||
updateCallback({ id: subscription.id, color: color })
|
||||
}
|
||||
/>
|
||||
</Td>
|
||||
<Td py={0} {...cellProps} wordBreak="break-word">
|
||||
{moment(subscription.created_at).format("L")}
|
||||
</Td>
|
||||
|
||||
<Td py={0} {...cellProps}>
|
||||
<ConfirmationRequest
|
||||
bodyMessage={"please confirm"}
|
||||
header={"Delete subscription"}
|
||||
onConfirm={() => deleteSubscription.mutate(subscription.id)}
|
||||
>
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
colorScheme="blue"
|
||||
icon={<DeleteIcon />}
|
||||
/>
|
||||
</ConfirmationRequest>
|
||||
</Td>
|
||||
</Tr>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SubscriptionCard;
|
|
@ -1,48 +1,24 @@
|
|||
import React, { useContext } from "react";
|
||||
import { Skeleton, IconButton, Container } from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
import { Skeleton, Container } from "@chakra-ui/react";
|
||||
import {
|
||||
Table,
|
||||
Th,
|
||||
Td,
|
||||
Tr,
|
||||
Thead,
|
||||
Tbody,
|
||||
Tooltip,
|
||||
Editable,
|
||||
EditableInput,
|
||||
EditablePreview,
|
||||
Image,
|
||||
Button,
|
||||
useMediaQuery,
|
||||
Accordion,
|
||||
} from "@chakra-ui/react";
|
||||
import { CheckIcon, DeleteIcon } from "@chakra-ui/icons";
|
||||
import moment from "moment";
|
||||
import CopyButton from "./CopyButton";
|
||||
import { useSubscriptions } from "../core/hooks";
|
||||
import ConfirmationRequest from "./ConfirmationRequest";
|
||||
import ColorSelector from "./ColorSelector";
|
||||
import OverlayContext from "../core/providers/OverlayProvider/context";
|
||||
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
|
||||
|
||||
const mapper = {
|
||||
"tag:erc721": "NFTs",
|
||||
"input:address": "Address",
|
||||
};
|
||||
import SubscriptionCard from "./SubscriptionCard";
|
||||
|
||||
const SubscriptionsList = ({ emptyCTA }) => {
|
||||
const overlay = useContext(OverlayContext);
|
||||
const {
|
||||
subscriptionsCache,
|
||||
updateSubscription,
|
||||
deleteSubscription,
|
||||
subscriptionTypeIcons,
|
||||
subscriptionTypeNames,
|
||||
} = useSubscriptions();
|
||||
const [isLargerThan530px] = useMediaQuery(["(min-width: 530px)"]);
|
||||
const { subscriptionsCache, subscriptionTypeIcons } = useSubscriptions();
|
||||
|
||||
const updateCallback = ({ id, label, color }) => {
|
||||
const data = { id: id };
|
||||
label && (data.label = label);
|
||||
color && (data.color = color);
|
||||
updateSubscription.mutate(data);
|
||||
const cellProps = {
|
||||
px: ["16px", "8px", "16px"],
|
||||
};
|
||||
|
||||
if (
|
||||
|
@ -50,121 +26,65 @@ const SubscriptionsList = ({ emptyCTA }) => {
|
|||
subscriptionsCache.data.subscriptions.length > 0
|
||||
) {
|
||||
return (
|
||||
<Table
|
||||
borderColor="gray.200"
|
||||
borderWidth="1px"
|
||||
variant="simple"
|
||||
colorScheme="blue"
|
||||
justifyContent="center"
|
||||
borderBottomRadius="xl"
|
||||
alignItems="baseline"
|
||||
h="auto"
|
||||
size="sm"
|
||||
mt={0}
|
||||
>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Token</Th>
|
||||
<Th>Label</Th>
|
||||
<Th>Address</Th>
|
||||
<Th>abi</Th>
|
||||
<Th>Color</Th>
|
||||
<Th>Date Created</Th>
|
||||
<Th>Actions</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{subscriptionsCache.data.subscriptions.map((subscription) => {
|
||||
const iconLink =
|
||||
subscriptionTypeIcons[subscription.subscription_type_id];
|
||||
return (
|
||||
<Tr key={`token-row-${subscription.id}`}>
|
||||
<Td>
|
||||
<Tooltip
|
||||
label={`${
|
||||
subscriptionTypeNames[subscription.subscription_type_id]
|
||||
}`}
|
||||
fontSize="md"
|
||||
>
|
||||
<Image h="32px" src={iconLink} alt="pool icon" />
|
||||
</Tooltip>
|
||||
</Td>
|
||||
<Td py={0}>
|
||||
<Editable
|
||||
colorScheme="blue"
|
||||
placeholder="enter note here"
|
||||
defaultValue={subscription.label}
|
||||
onSubmit={(nextValue) =>
|
||||
updateCallback({
|
||||
id: subscription.id,
|
||||
label: nextValue,
|
||||
})
|
||||
}
|
||||
>
|
||||
<EditablePreview
|
||||
maxW="40rem"
|
||||
_placeholder={{ color: "black" }}
|
||||
/>
|
||||
<EditableInput maxW="40rem" />
|
||||
</Editable>
|
||||
</Td>
|
||||
<Td mr={4} p={0}>
|
||||
{subscription.address?.startsWith("tag") ? (
|
||||
<CopyButton>{mapper[subscription.address]}</CopyButton>
|
||||
) : (
|
||||
<CopyButton>{subscription.address}</CopyButton>
|
||||
)}
|
||||
</Td>
|
||||
<Td mr={4} p={0}>
|
||||
{subscription.abi ? (
|
||||
<CheckIcon />
|
||||
) : (
|
||||
<Button
|
||||
colorScheme="orange"
|
||||
size="xs"
|
||||
py={2}
|
||||
disabled={!subscription.address}
|
||||
onClick={() =>
|
||||
overlay.toggleModal({
|
||||
type: MODAL_TYPES.UPLOAD_ABI,
|
||||
props: { id: subscription.id },
|
||||
})
|
||||
}
|
||||
>
|
||||
Upload
|
||||
</Button>
|
||||
)}
|
||||
</Td>
|
||||
<Td>
|
||||
<ColorSelector
|
||||
// subscriptionId={subscription.id}
|
||||
initialColor={subscription.color}
|
||||
callback={(color) =>
|
||||
updateCallback({ id: subscription.id, color: color })
|
||||
}
|
||||
/>
|
||||
</Td>
|
||||
<Td py={0}>{moment(subscription.created_at).format("L")}</Td>
|
||||
|
||||
<Td py={0}>
|
||||
<ConfirmationRequest
|
||||
bodyMessage={"please confirm"}
|
||||
header={"Delete subscription"}
|
||||
onConfirm={() => deleteSubscription.mutate(subscription.id)}
|
||||
>
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
colorScheme="blue"
|
||||
icon={<DeleteIcon />}
|
||||
/>
|
||||
</ConfirmationRequest>
|
||||
</Td>
|
||||
<>
|
||||
{isLargerThan530px && (
|
||||
<Table
|
||||
borderColor="gray.200"
|
||||
borderWidth="1px"
|
||||
variant="simple"
|
||||
colorScheme="blue"
|
||||
justifyContent="center"
|
||||
borderBottomRadius="xl"
|
||||
alignItems="baseline"
|
||||
h="auto"
|
||||
size="sm"
|
||||
mt={0}
|
||||
>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th {...cellProps}>Token</Th>
|
||||
<Th {...cellProps}>Label</Th>
|
||||
<Th {...cellProps}>Address</Th>
|
||||
<Th {...cellProps}>abi</Th>
|
||||
<Th {...cellProps}>Color</Th>
|
||||
<Th {...cellProps}>Date Created</Th>
|
||||
<Th {...cellProps}>Actions</Th>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</Thead>
|
||||
|
||||
<Tbody>
|
||||
{subscriptionsCache.data.subscriptions.map((subscription) => {
|
||||
const iconLink =
|
||||
subscriptionTypeIcons[subscription.subscription_type_id];
|
||||
return (
|
||||
<SubscriptionCard
|
||||
key={`token-row-${subscription.id}`}
|
||||
subscription={subscription}
|
||||
isDesktopView={isLargerThan530px}
|
||||
iconLink={iconLink}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Tbody>
|
||||
</Table>
|
||||
)}
|
||||
{!isLargerThan530px && (
|
||||
<Accordion allowToggle={true}>
|
||||
{subscriptionsCache.data.subscriptions.map((subscription) => {
|
||||
const iconLink =
|
||||
subscriptionTypeIcons[subscription.subscription_type_id];
|
||||
return (
|
||||
<SubscriptionCard
|
||||
key={`token-row-${subscription.id}`}
|
||||
subscription={subscription}
|
||||
isDesktopView={isLargerThan530px}
|
||||
iconLink={iconLink}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Accordion>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
} else if (
|
||||
subscriptionsCache.data &&
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
import { React } from "react";
|
||||
import { Flex, Image, Link, LinkBox, LinkOverlay } from "@chakra-ui/react";
|
||||
import {
|
||||
Flex,
|
||||
Image,
|
||||
Link,
|
||||
LinkBox,
|
||||
LinkOverlay,
|
||||
chakra,
|
||||
} from "@chakra-ui/react";
|
||||
|
||||
const TrustedBadge = ({
|
||||
name,
|
||||
|
@ -8,10 +15,15 @@ const TrustedBadge = ({
|
|||
scale,
|
||||
isGrayScale,
|
||||
boxURL,
|
||||
invertColors,
|
||||
...props
|
||||
}) => {
|
||||
const _scale = scale ?? 1;
|
||||
const _isGrayScale = isGrayScale ? "grayscale(100%)" : "";
|
||||
const _invert = invertColors ? "invert(100%)" : "";
|
||||
const filterStr = _isGrayScale + " " + _invert;
|
||||
return (
|
||||
<LinkBox m={2}>
|
||||
<LinkBox m={2} borderRadius="md" {...props}>
|
||||
<LinkOverlay href={boxURL} isExternal>
|
||||
<Flex
|
||||
m={1}
|
||||
|
@ -23,7 +35,7 @@ const TrustedBadge = ({
|
|||
direction="column"
|
||||
>
|
||||
<Image
|
||||
sx={isGrayScale && { filter: "grayscale(100%)" }}
|
||||
sx={{ filter: filterStr }}
|
||||
h={[
|
||||
`${2.25 * _scale}rem`,
|
||||
null,
|
||||
|
@ -51,4 +63,4 @@ const TrustedBadge = ({
|
|||
</LinkBox>
|
||||
);
|
||||
};
|
||||
export default TrustedBadge;
|
||||
export default chakra(TrustedBadge);
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import React, { useContext, useState, useEffect, useRef } from "react";
|
||||
import { Button, Input } from "@chakra-ui/react";
|
||||
import OverlayContext from "../core/providers/OverlayProvider/context";
|
||||
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
|
||||
import { useSubscriptions } from "../core/hooks";
|
||||
|
||||
const MobileFiledInput = ({
|
||||
onChange,
|
||||
initialValue,
|
||||
cancelText,
|
||||
submitText,
|
||||
id,
|
||||
}) => {
|
||||
const { updateSubscription } = useSubscriptions();
|
||||
const isLoading = updateSubscription.isLoading;
|
||||
const [value, setValue] = useState(initialValue);
|
||||
const updateCallback = () => {
|
||||
const data = { id: id };
|
||||
value && (data.label = value);
|
||||
updateSubscription.mutate(data);
|
||||
};
|
||||
const [wasSubmitted, setWasSubmitted] = useState(false);
|
||||
console.log("MobileFiledInput", isLoading, wasSubmitted);
|
||||
const inputRef = useRef();
|
||||
const handleChange = (e) => {
|
||||
setValue(e.target.value);
|
||||
onChange && onChange(e);
|
||||
};
|
||||
|
||||
const overlay = useContext(OverlayContext);
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
updateCallback({});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
setWasSubmitted(true);
|
||||
}
|
||||
}, [isLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && wasSubmitted) {
|
||||
overlay.toggleModal({ type: MODAL_TYPES.OFF });
|
||||
setWasSubmitted(false);
|
||||
}
|
||||
}, [isLoading, overlay, wasSubmitted]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
placeholder="Enter a value"
|
||||
/>
|
||||
<Button
|
||||
colorScheme="green"
|
||||
onClick={(e) => handleSubmit(e)}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
{submitText}
|
||||
</Button>
|
||||
<Button
|
||||
isDisabled={isLoading}
|
||||
onClick={() => overlay.toggleModal({ type: MODAL_TYPES.OFF })}
|
||||
colorScheme="blue"
|
||||
>
|
||||
{cancelText}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileFiledInput;
|
|
@ -6,51 +6,87 @@ export const BUGOUT_ENDPOINTS = {
|
|||
};
|
||||
|
||||
export const DEFAULT_METATAGS = {
|
||||
title: "Moonstream: Building blocks for your blockchain economy",
|
||||
title: "Moonstream: Building blocks for your blockchain game",
|
||||
description:
|
||||
"Moonstream DAO makes tools that help you build, manage, and secure your blockchain economy.",
|
||||
"Moonstream DAO makes tools that help you build, manage, and secure your blockchain game.",
|
||||
keywords:
|
||||
"analytics, blockchain analytics, protocol, protocols, blockchain, crypto, data, NFT gaming, smart contracts, web3, smart contract, ethereum, polygon, matic, transactions, defi, finance, decentralized, mempool, NFT, NFTs, DAO, DAOs, cryptocurrency, cryptocurrencies, bitcoin, blockchain economy, marketplace, blockchain security, loyalty program, Ethereum bridge, Ethereum bridges, NFT game, NFT games",
|
||||
"analytics, blockchain analytics, protocol, protocols, blockchain, crypto, data, NFT gaming, smart contracts, web3, smart contract, ethereum, polygon, matic, transactions, defi, finance, decentralized, mempool, NFT, NFTs, DAO, DAOs, cryptocurrency, cryptocurrencies, bitcoin, blockchain economy, blockchain game, marketplace, blockchain security, loyalty program, Ethereum bridge, Ethereum bridges, NFT game, NFT games",
|
||||
url: "https://www.moonstream.to",
|
||||
image: `https://s3.amazonaws.com/static.simiotics.com/moonstream/assets/crypto+traders.png`,
|
||||
};
|
||||
|
||||
export const FOOTER_COLUMNS = {
|
||||
NEWS: "News",
|
||||
COMPANY: "Company",
|
||||
PRODUCT: "Product",
|
||||
// export const FOOTER_COLUMNS = {
|
||||
// NEWS: "News",
|
||||
// COMPANY: "Company",
|
||||
// PRODUCT: "Product",
|
||||
// };
|
||||
|
||||
export const SIITEMAP_CATEGORIES = {
|
||||
SOLUTIONS: "Solutions",
|
||||
DEVELOPERS: "Developers",
|
||||
RESOURCES: "Resources",
|
||||
ABOUT: "About",
|
||||
};
|
||||
|
||||
export const ALL_NAV_PATHES = [
|
||||
export const PAGETYPE = {
|
||||
EMPTY: 0,
|
||||
CONTENT: 1,
|
||||
EXTERNAL: 2,
|
||||
};
|
||||
|
||||
export const SITEMAP = [
|
||||
{
|
||||
title: "Product",
|
||||
path: "/product",
|
||||
footerCategory: FOOTER_COLUMNS.PRODUCT,
|
||||
title: "Resources",
|
||||
path: "/resources",
|
||||
type: PAGETYPE.EMPTY,
|
||||
children: [
|
||||
{
|
||||
title: "Case studies",
|
||||
path: "https://docs.google.com/document/d/1mjfF8SgRrAZvtCVVxB2qNSUcbbmrH6dTEYSMfHKdEgc",
|
||||
type: PAGETYPE.EXTERNAL,
|
||||
},
|
||||
{
|
||||
title: "Whitepapers",
|
||||
path: "/whitepapers",
|
||||
type: PAGETYPE.CONTENT,
|
||||
},
|
||||
{
|
||||
title: "Blog",
|
||||
path: "https://blog.moonstream.to",
|
||||
type: PAGETYPE.EXTERNAL,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Team",
|
||||
path: "/team",
|
||||
footerCategory: FOOTER_COLUMNS.COMPANY,
|
||||
title: "Developers",
|
||||
path: "/developers",
|
||||
type: PAGETYPE.EMPTY,
|
||||
|
||||
children: [
|
||||
{
|
||||
title: "Docs",
|
||||
path: "/docs",
|
||||
type: PAGETYPE.CONTENT,
|
||||
},
|
||||
{
|
||||
title: "Status",
|
||||
path: "/status",
|
||||
type: PAGETYPE.CONTENT,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
title: "Docs",
|
||||
path: "/docs",
|
||||
footerCategory: FOOTER_COLUMNS.PRODUCT,
|
||||
},
|
||||
{
|
||||
title: "Whitepapers",
|
||||
path: "/whitepapers",
|
||||
footerCategory: FOOTER_COLUMNS.PRODUCT,
|
||||
},
|
||||
{
|
||||
title: "Blog",
|
||||
path: "https://blog.moonstream.to",
|
||||
footerCategory: FOOTER_COLUMNS.NEWS,
|
||||
},
|
||||
{
|
||||
title: "Status",
|
||||
path: "/status",
|
||||
footerCategory: FOOTER_COLUMNS.PRODUCT,
|
||||
title: "About",
|
||||
path: "/about",
|
||||
type: PAGETYPE.EMPTY,
|
||||
children: [
|
||||
{
|
||||
title: "Team",
|
||||
path: "/team",
|
||||
type: PAGETYPE.CONTENT,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -7,18 +7,6 @@ const useStatus = () => {
|
|||
const response = await StatusService.serverListStatus();
|
||||
return response.data;
|
||||
};
|
||||
const getCrawlersStatus = async () => {
|
||||
const response = await StatusService.crawlersStatus();
|
||||
return response.data;
|
||||
};
|
||||
const getDBServerStatus = async () => {
|
||||
const response = await StatusService.dbServerStatus();
|
||||
return response.data;
|
||||
};
|
||||
const getLatestBlockDBStatus = async () => {
|
||||
const response = await StatusService.latestBlockDBStatus();
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const serverListStatusCache = useQuery(
|
||||
"serverListStatus",
|
||||
|
@ -28,28 +16,9 @@ const useStatus = () => {
|
|||
retry: 0,
|
||||
}
|
||||
);
|
||||
const crawlersStatusCache = useQuery("crawlers", getCrawlersStatus, {
|
||||
...queryCacheProps,
|
||||
retry: 0,
|
||||
});
|
||||
const dbServerStatusCache = useQuery("dbServer", getDBServerStatus, {
|
||||
...queryCacheProps,
|
||||
retry: 0,
|
||||
});
|
||||
const latestBlockDBStatusCache = useQuery(
|
||||
"latestBlockDB",
|
||||
getLatestBlockDBStatus,
|
||||
{
|
||||
...queryCacheProps,
|
||||
retry: 0,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
serverListStatusCache,
|
||||
crawlersStatusCache,
|
||||
dbServerStatusCache,
|
||||
latestBlockDBStatusCache,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ import { useClientID, useUser, useRouter } from "../../hooks";
|
|||
import { MIXPANEL_EVENTS, MIXPANEL_PROPS } from "./constants";
|
||||
import UIContext from "../UIProvider/context";
|
||||
|
||||
const TELEMETRY_SCHEMA_VERSION = 1.0;
|
||||
|
||||
const AnalyticsProvider = ({ children }) => {
|
||||
const clientID = useClientID();
|
||||
const analytics = process.env.NEXT_PUBLIC_MIXPANEL_TOKEN;
|
||||
|
@ -138,6 +140,7 @@ const AnalyticsProvider = ({ children }) => {
|
|||
loaded: () => {
|
||||
setIsLoaded(true);
|
||||
mixpanel.identify(clientID);
|
||||
mixpanel.register({ schema_version: TELEMETRY_SCHEMA_VERSION });
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
|
|
|
@ -9,6 +9,7 @@ export const MODAL_TYPES = {
|
|||
NEW_SUBSCRIPTON: 7,
|
||||
UPLOAD_ABI: 8,
|
||||
NEW_DASHBOARD_FLOW: 9,
|
||||
MOBILE_INPUT_FIELD: 10,
|
||||
};
|
||||
|
||||
export const DRAWER_TYPES = {
|
||||
|
|
|
@ -38,6 +38,7 @@ import SignUp from "../../../components/SignUp";
|
|||
import NewDashboardChart from "../../../components/NewDashboardChart";
|
||||
import { useRouter } from "../../hooks";
|
||||
import { DASHBOARD_UPDATE_ACTIONS } from "../../constants";
|
||||
import UpdateSubscriptionLabelInput from "../../../components/UpdateSubscriptionLabelInput";
|
||||
const NewDashboardName = React.lazy(() =>
|
||||
import("../../../components/NewDashboardName")
|
||||
);
|
||||
|
@ -64,6 +65,7 @@ const OverlayProvider = ({ children }) => {
|
|||
const [modal, toggleModal] = useState({
|
||||
type: MODAL_TYPES.OFF,
|
||||
props: undefined,
|
||||
key: undefined,
|
||||
});
|
||||
const [drawer, toggleDrawer] = useState({
|
||||
type: DRAWER_TYPES.OFF,
|
||||
|
@ -224,6 +226,8 @@ const OverlayProvider = ({ children }) => {
|
|||
});
|
||||
};
|
||||
|
||||
console.log("_key:", modal._key);
|
||||
|
||||
return (
|
||||
<OverlayContext.Provider
|
||||
value={{ modal, toggleModal, drawer, toggleDrawer, toggleAlert }}
|
||||
|
@ -279,6 +283,7 @@ const OverlayProvider = ({ children }) => {
|
|||
{modal.type === MODAL_TYPES.UPLOAD_ABI && "Assign ABI"}
|
||||
{modal.type === MODAL_TYPES.NEW_DASHBOARD_FLOW &&
|
||||
"Would you like to give it a name?"}
|
||||
{modal.type === MODAL_TYPES.MOBILE_INPUT_FIELD && modal.props.title}
|
||||
</ModalHeader>
|
||||
<Divider />
|
||||
<ModalCloseButton />
|
||||
|
@ -316,6 +321,9 @@ const OverlayProvider = ({ children }) => {
|
|||
{modal.type === MODAL_TYPES.NEW_DASHBOARD_FLOW && (
|
||||
<NewDashboardName {...modal.props} />
|
||||
)}
|
||||
{modal.type === MODAL_TYPES.MOBILE_INPUT_FIELD && (
|
||||
<UpdateSubscriptionLabelInput {...modal.props} />
|
||||
)}
|
||||
</Suspense>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
|
|
|
@ -1,33 +1,10 @@
|
|||
import { http } from "../utils";
|
||||
|
||||
const BUGOUT_STATUS_URL = process.env.NEXT_PUBLIC_BUGOUT_STATUS_URL;
|
||||
const API_URL = process.env.NEXT_PUBLIC_MOONSTREAM_API_URL;
|
||||
const DB_URL = process.env.NEXT_PUBLIC_MOONSTREAM_DB_URL;
|
||||
|
||||
export const serverListStatus = () => {
|
||||
return http({
|
||||
method: "GET",
|
||||
url: `${BUGOUT_STATUS_URL}`,
|
||||
});
|
||||
};
|
||||
|
||||
export const crawlersStatus = () => {
|
||||
return http({
|
||||
method: "GET",
|
||||
url: `${API_URL}/status`,
|
||||
});
|
||||
};
|
||||
|
||||
export const dbServerStatus = () => {
|
||||
return http({
|
||||
method: "GET",
|
||||
url: `${DB_URL}/ping`,
|
||||
});
|
||||
};
|
||||
|
||||
export const latestBlockDBStatus = () => {
|
||||
return http({
|
||||
method: "GET",
|
||||
url: `${DB_URL}/block/latest`,
|
||||
url: `${BUGOUT_STATUS_URL}/status`,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Flex, Spinner, Box } from "@chakra-ui/react";
|
|||
import { getLayout as getSiteLayout } from "./RootLayout";
|
||||
import React, { useContext, useEffect } from "react";
|
||||
import UIContext from "../core/providers/UIProvider/context";
|
||||
import AppNavbar from "../components/AppNavbar";
|
||||
|
||||
const AppLayout = ({ children }) => {
|
||||
const ui = useContext(UIContext);
|
||||
|
@ -16,12 +17,12 @@ const AppLayout = ({ children }) => {
|
|||
|
||||
return (
|
||||
<Flex
|
||||
direction="row"
|
||||
id="JournalsWrapper"
|
||||
flexGrow={1}
|
||||
maxH="100%"
|
||||
w="100%"
|
||||
overflow="hidden"
|
||||
direction="column"
|
||||
>
|
||||
{(!ui.isAppReady || !ui.isLoggedIn) && (
|
||||
<Spinner
|
||||
|
@ -44,7 +45,9 @@ const AppLayout = ({ children }) => {
|
|||
zIndex={1010}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Flex direction={"column"} bgColor="blue.900" id="Navbar">
|
||||
<AppNavbar />
|
||||
</Flex>
|
||||
{ui.isAppReady && ui.isLoggedIn && children}
|
||||
</Flex>
|
||||
);
|
||||
|
|
|
@ -3,7 +3,6 @@ import { Flex, Center, Text, Link, IconButton } from "@chakra-ui/react";
|
|||
import React, { Suspense, useContext, useState } from "react";
|
||||
import UIContext from "../core/providers/UIProvider/context";
|
||||
const Sidebar = React.lazy(() => import("../components/Sidebar"));
|
||||
const Navbar = React.lazy(() => import("../components/Navbar"));
|
||||
|
||||
const RootLayout = (props) => {
|
||||
const ui = useContext(UIContext);
|
||||
|
@ -27,9 +26,6 @@ const RootLayout = (props) => {
|
|||
flexBasis="100px"
|
||||
overflowX="hidden"
|
||||
>
|
||||
<Suspense fallback="">
|
||||
<Navbar />
|
||||
</Suspense>
|
||||
{!ui.isAppView && (
|
||||
<Flex
|
||||
w="100%"
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
import React, { useState, useEffect, useLayoutEffect } from "react";
|
||||
import { Flex, useMediaQuery, Stack } from "@chakra-ui/react";
|
||||
import { DEFAULT_METATAGS, AWS_ASSETS_PATH } from "../core/constants";
|
||||
import { getLayout as getSiteLayout } from "./index";
|
||||
|
||||
const assets = {
|
||||
background720: `${AWS_ASSETS_PATH}/blog-background-720x405.png`,
|
||||
background1920: `${AWS_ASSETS_PATH}/blog-background-720x405.png`,
|
||||
background2880: `${AWS_ASSETS_PATH}/blog-background-720x405.png`,
|
||||
background3840: `${AWS_ASSETS_PATH}/blog-background-720x405.png`,
|
||||
};
|
||||
|
||||
const InfoPageLayout = ({ children }) => {
|
||||
const [background, setBackground] = useState("background720");
|
||||
const [backgroundLoaded720, setBackgroundLoaded720] = useState(false);
|
||||
const [backgroundLoaded1920, setBackgroundLoaded1920] = useState(false);
|
||||
const [backgroundLoaded2880, setBackgroundLoaded2880] = useState(false);
|
||||
const [backgroundLoaded3840, setBackgroundLoaded3840] = useState(false);
|
||||
|
||||
const [
|
||||
isLargerThan720px,
|
||||
isLargerThan1920px,
|
||||
isLargerThan2880px,
|
||||
isLargerThan3840px,
|
||||
] = useMediaQuery([
|
||||
"(min-width: 720px)",
|
||||
"(min-width: 1920px)",
|
||||
"(min-width: 2880px)",
|
||||
"(min-width: 3840px)",
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
assets["background720"] = `${AWS_ASSETS_PATH}/blog-background-720x405.png`;
|
||||
assets[
|
||||
"background1920"
|
||||
] = `${AWS_ASSETS_PATH}/blog-background-1920x1080.png`;
|
||||
assets[
|
||||
"background2880"
|
||||
] = `${AWS_ASSETS_PATH}/blog-background-2880x1620.png`;
|
||||
assets[
|
||||
"background3840"
|
||||
] = `${AWS_ASSETS_PATH}/blog-background-3840x2160.png`;
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (backgroundLoaded3840) {
|
||||
setBackground("background3840");
|
||||
} else if (backgroundLoaded2880) {
|
||||
setBackground("background2880");
|
||||
} else if (backgroundLoaded1920) {
|
||||
setBackground("background1920");
|
||||
} else {
|
||||
setBackground("background720");
|
||||
}
|
||||
}, [
|
||||
isLargerThan720px,
|
||||
isLargerThan1920px,
|
||||
isLargerThan2880px,
|
||||
isLargerThan3840px,
|
||||
backgroundLoaded720,
|
||||
backgroundLoaded1920,
|
||||
backgroundLoaded2880,
|
||||
backgroundLoaded3840,
|
||||
]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const imageLoader720 = new Image();
|
||||
imageLoader720.src = `${AWS_ASSETS_PATH}/blog-background-720x405.png`;
|
||||
imageLoader720.onload = () => {
|
||||
setBackgroundLoaded720(true);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const imageLoader1920 = new Image();
|
||||
imageLoader1920.src = `${AWS_ASSETS_PATH}/blog-background-1920x1080.png`;
|
||||
imageLoader1920.onload = () => {
|
||||
setBackgroundLoaded1920(true);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const imageLoader2880 = new Image();
|
||||
imageLoader2880.src = `${AWS_ASSETS_PATH}/blog-background-2880x1620.png`;
|
||||
imageLoader2880.onload = () => {
|
||||
setBackgroundLoaded2880(true);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const imageLoader3840 = new Image();
|
||||
imageLoader3840.src = `${AWS_ASSETS_PATH}/blog-background-3840x2160.png`;
|
||||
imageLoader3840.onload = () => {
|
||||
setBackgroundLoaded3840(true);
|
||||
};
|
||||
}, []);
|
||||
const margin = 0;
|
||||
return (
|
||||
<Flex
|
||||
bgPos="bottom"
|
||||
bgColor="transparent"
|
||||
bgSize="cover"
|
||||
backgroundImage={`url(${assets[`${background}`]})`}
|
||||
minH="100vh"
|
||||
direction="column"
|
||||
alignItems="center"
|
||||
w="100%"
|
||||
>
|
||||
<Stack
|
||||
mx={margin}
|
||||
my={[4, 6, 12]}
|
||||
maxW="1700px"
|
||||
textAlign="justify"
|
||||
minH={"100vh"}
|
||||
>
|
||||
{children}
|
||||
</Stack>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const getLayout = (page) =>
|
||||
getSiteLayout(<InfoPageLayout>{page}</InfoPageLayout>);
|
||||
|
||||
export const getLayoutProps = () => {
|
||||
const assetPreload = Object.keys(assets).map((key) => {
|
||||
return {
|
||||
rel: "preload",
|
||||
href: assets[key],
|
||||
as: "image",
|
||||
};
|
||||
});
|
||||
const preconnects = [{ rel: "preconnect", href: "https://s3.amazonaws.com" }];
|
||||
|
||||
const preloads = assetPreload.concat(preconnects);
|
||||
|
||||
return {
|
||||
props: { metaTags: { ...DEFAULT_METATAGS }, preloads },
|
||||
};
|
||||
};
|
||||
export default InfoPageLayout;
|
|
@ -2,11 +2,12 @@ import React from "react";
|
|||
import Footer from "../components/Footer";
|
||||
import Scrollable from "../components/Scrollable";
|
||||
import RootLayout from "./RootLayout";
|
||||
|
||||
import Navbar from "../components/Navbar";
|
||||
const LayoutWrapper = ({ children }) => {
|
||||
return (
|
||||
<RootLayout>
|
||||
<Scrollable>
|
||||
<Navbar />
|
||||
{children}
|
||||
<Footer />
|
||||
</Scrollable>
|
||||
|
|
|
@ -1,5 +1,65 @@
|
|||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,go
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,go
|
||||
|
||||
### Go ###
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
### Go Patch ###
|
||||
/vendor/
|
||||
/Godeps/
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
|
||||
# Support for Project snippet scope
|
||||
.vscode/*.code-snippets
|
||||
|
||||
# Ignore code-workspaces
|
||||
*.code-workspace
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,go
|
||||
|
||||
# Custom
|
||||
.secrets/*
|
||||
dev.env
|
||||
prod.env
|
||||
test.env
|
||||
test.env
|
||||
nodebalancer
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ PARAMETERS_ENV_PATH="${SECRETS_DIR}/app.env"
|
|||
AWS_SSM_PARAMETER_PATH="${AWS_SSM_PARAMETER_PATH:-/moonstream/prod}"
|
||||
SCRIPT_DIR="$(realpath $(dirname $0))"
|
||||
PARAMETERS_SCRIPT="${SCRIPT_DIR}/parameters.py"
|
||||
NODE_BALANCER_CONFIG_PATH="${NODE_BALANCER_CONFIG_PATH:-/home/ubuntu/.nodebalancer}"
|
||||
NODE_BALANCER_CONFIG_SOURCE_FILE="node-balancer-config.txt"
|
||||
|
||||
# Service file
|
||||
NODE_BALANCER_SERVICE_FILE="node-balancer.service"
|
||||
|
@ -59,6 +61,15 @@ cd "${APP_NODES_DIR}/node_balancer"
|
|||
HOME=/root /usr/local/go/bin/go build -o "${APP_NODES_DIR}/node_balancer/nodebalancer" "${APP_NODES_DIR}/node_balancer/main.go"
|
||||
cd "${EXEC_DIR}"
|
||||
|
||||
echo
|
||||
echo
|
||||
echo -e "${PREFIX_INFO} Update nodebalancer configuration file"
|
||||
if [ ! -d "$NODE_BALANCER_CONFIG_PATH" ]; then
|
||||
mkdir "$NODE_BALANCER_CONFIG_PATH"
|
||||
echo -e "${PREFIX_WARN} Created new node balancer config directory"
|
||||
fi
|
||||
cp "${SCRIPT_DIR}/${NODE_BALANCER_CONFIG_SOURCE_FILE}" "${NODE_BALANCER_CONFIG_PATH}/config.txt"
|
||||
|
||||
echo
|
||||
echo
|
||||
echo -e "${PREFIX_INFO} Replacing existing load balancer for nodes service definition with ${NODE_BALANCER_SERVICE_FILE}"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
ethereum,a.ethereum.moonstream.internal,8545
|
||||
ethereum,b.ethereum.moonstream.internal,8545
|
||||
polygon,a.polygon.moonstream.internal,8545
|
||||
polygon,b.polygon.moonstream.internal,8545
|
||||
xdai,a.xdai.moonstream.internal,8545
|
|
@ -11,8 +11,12 @@ WorkingDirectory=/home/ubuntu/moonstream/nodes/node_balancer
|
|||
EnvironmentFile=/home/ubuntu/moonstream-secrets/app.env
|
||||
Restart=on-failure
|
||||
RestartSec=15s
|
||||
ExecStart=/home/ubuntu/moonstream/nodes/node_balancer/nodebalancer -host "${AWS_LOCAL_IPV4}" -port 8544 -healthcheck
|
||||
ExecStart=/home/ubuntu/moonstream/nodes/node_balancer/nodebalancer server \
|
||||
-host "${AWS_LOCAL_IPV4}" \
|
||||
-port 8544 \
|
||||
-healthcheck \
|
||||
-config /home/ubuntu/.nodebalancer/config.txt
|
||||
SyslogIdentifier=node-balancer
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Deployment script - intended to run on Moonstream Gnosis node server
|
||||
|
||||
# Colors
|
||||
C_RESET='\033[0m'
|
||||
C_RED='\033[1;31m'
|
||||
C_GREEN='\033[1;32m'
|
||||
C_YELLOW='\033[1;33m'
|
||||
|
||||
# Logs
|
||||
PREFIX_INFO="${C_GREEN}[INFO]${C_RESET} [$(date +%d-%m\ %T)]"
|
||||
PREFIX_WARN="${C_YELLOW}[WARN]${C_RESET} [$(date +%d-%m\ %T)]"
|
||||
PREFIX_CRIT="${C_RED}[CRIT]${C_RESET} [$(date +%d-%m\ %T)]"
|
||||
|
||||
# Main
|
||||
AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-us-east-1}"
|
||||
APP_DIR="${APP_DIR:-/home/ubuntu/moonstream}"
|
||||
APP_NODES_DIR="${APP_DIR}/nodes"
|
||||
SECRETS_DIR="${SECRETS_DIR:-/home/ubuntu/moonstream-secrets}"
|
||||
PARAMETERS_ENV_PATH="${SECRETS_DIR}/app.env"
|
||||
SCRIPT_DIR="$(realpath $(dirname $0))"
|
||||
|
||||
# Node status server service file
|
||||
NODE_STATUS_SERVER_SERVICE_FILE="node-status.service"
|
||||
|
||||
# Gnosis nethermind service file
|
||||
XDAI_NETHERMIND_SERVICE_FILE="xdai.service"
|
||||
|
||||
set -eu
|
||||
|
||||
echo
|
||||
echo
|
||||
echo -e "${PREFIX_INFO} Building executable server of node status server"
|
||||
EXEC_DIR=$(pwd)
|
||||
cd "${APP_NODES_DIR}/server"
|
||||
HOME=/root /usr/local/go/bin/go build -o "${APP_NODES_DIR}/server/nodestatus" "${APP_NODES_DIR}/server/main.go"
|
||||
cd "${EXEC_DIR}"
|
||||
|
||||
echo
|
||||
echo
|
||||
echo -e "${PREFIX_INFO} Create secrets directory"
|
||||
mkdir -p "${SECRETS_DIR}"
|
||||
|
||||
echo
|
||||
echo
|
||||
echo -e "${PREFIX_INFO} Install checkenv"
|
||||
HOME=/root /usr/local/go/bin/go install github.com/bugout-dev/checkenv@latest
|
||||
|
||||
echo
|
||||
echo
|
||||
echo -e "${PREFIX_INFO} Retrieving deployment parameters"
|
||||
AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}" /root/go/bin/checkenv show aws_ssm+Product:moonstream,Node:true > "${PARAMETERS_ENV_PATH}"
|
||||
|
||||
echo
|
||||
echo
|
||||
echo -e "${PREFIX_INFO} Add instance local IP to parameters"
|
||||
AWS_LOCAL_IPV4="$(ec2metadata --local-ipv4)"
|
||||
echo "AWS_LOCAL_IPV4=$AWS_LOCAL_IPV4" >> "${PARAMETERS_ENV_PATH}"
|
||||
|
||||
echo
|
||||
echo
|
||||
echo -e "${PREFIX_INFO} Replacing existing node status server definition with ${NODE_STATUS_SERVER_SERVICE_FILE}"
|
||||
chmod 644 "${SCRIPT_DIR}/${NODE_STATUS_SERVER_SERVICE_FILE}"
|
||||
cp "${SCRIPT_DIR}/${NODE_STATUS_SERVER_SERVICE_FILE}" "/etc/systemd/system/${NODE_STATUS_SERVER_SERVICE_FILE}"
|
||||
systemctl daemon-reload
|
||||
systemctl restart "${NODE_STATUS_SERVER_SERVICE_FILE}"
|
||||
|
||||
echo
|
||||
echo
|
||||
echo -e "${PREFIX_INFO} Replacing existing Gnosis nethermind service definition with ${XDAI_NETHERMIND_SERVICE_FILE}"
|
||||
chmod 644 "${SCRIPT_DIR}/${XDAI_NETHERMIND_SERVICE_FILE}"
|
||||
cp "${SCRIPT_DIR}/${XDAI_NETHERMIND_SERVICE_FILE}" "/etc/systemd/system/${XDAI_NETHERMIND_SERVICE_FILE}"
|
||||
systemctl daemon-reload
|
||||
systemctl disable "${XDAI_NETHERMIND_SERVICE_FILE}"
|
||||
echo -e "${PREFIX_WARN} Nethermind service updated, but not restarted!"
|
|
@ -0,0 +1,17 @@
|
|||
[Unit]
|
||||
Description=Moonstream node status server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=ubuntu
|
||||
Group=www-data
|
||||
WorkingDirectory=/home/ubuntu/moonstream/nodes/server
|
||||
EnvironmentFile=/home/ubuntu/moonstream-secrets/app.env
|
||||
ExecStart=/home/ubuntu/moonstream/nodes/server/nodestatus \
|
||||
-blockchain xdai \
|
||||
-host "${AWS_LOCAL_IPV4}" \
|
||||
-port "${MOONSTREAM_NODES_SERVER_PORT}"
|
||||
SyslogIdentifier=node-status
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,25 @@
|
|||
[Unit]
|
||||
Description=Gnosis node nethermind client
|
||||
StartLimitIntervalSec=500
|
||||
StartLimitBurst=5
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
EnvironmentFile=/home/ubuntu/moonstream-secrets/app.env
|
||||
ExecStart=/usr/bin/nethermind \
|
||||
--config /usr/share/nethermind/configs/xdai.cfg \
|
||||
--datadir /mnt/disks/nodes/xdai \
|
||||
--JsonRpc.Enabled true \
|
||||
--JsonRpc.EnabledModules Eth,Web3,TxPool \
|
||||
--JsonRpc.Host "${AWS_LOCAL_IPV4}" \
|
||||
--JsonRpc.Port 8545
|
||||
Type=simple
|
||||
User=ubuntu
|
||||
ExecStop=/bin/kill -s SIGINT -$MAINPID
|
||||
TimeoutStopSec=180
|
||||
SyslogIdentifier=xdai
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,143 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/bugout-dev/moonstream/nodes/node_balancer/configs"
|
||||
)
|
||||
|
||||
var (
|
||||
stateCLI StateCLI
|
||||
)
|
||||
|
||||
type flagSlice []string
|
||||
|
||||
func (i *flagSlice) String() string {
|
||||
return strings.Join(*i, ", ")
|
||||
}
|
||||
|
||||
func (i *flagSlice) Set(value string) error {
|
||||
*i = append(*i, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Command Line Interface state
|
||||
type StateCLI struct {
|
||||
serverCmd *flag.FlagSet
|
||||
versionCmd *flag.FlagSet
|
||||
|
||||
// Common flags
|
||||
configPathFlag string
|
||||
helpFlag bool
|
||||
|
||||
// Server flags
|
||||
listeningAddrFlag string
|
||||
listeningPortFlag string
|
||||
nodesFlag flagSlice
|
||||
enableHealthCheckFlag bool
|
||||
enableDebugFlag bool
|
||||
}
|
||||
|
||||
func (s *StateCLI) usage() {
|
||||
fmt.Printf(`usage: nodebalancer [-h] {%[1]s,%[2]s} ...
|
||||
Moonstream node balancer CLI
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
|
||||
subcommands:
|
||||
{%[1]s,%[2]s}
|
||||
`, s.serverCmd.Name(), s.versionCmd.Name())
|
||||
}
|
||||
|
||||
func (s *StateCLI) checkRequirements() {
|
||||
if s.helpFlag {
|
||||
switch {
|
||||
case s.serverCmd.Parsed():
|
||||
fmt.Printf("Start nodebalancer server\n\n")
|
||||
s.serverCmd.PrintDefaults()
|
||||
os.Exit(0)
|
||||
case s.versionCmd.Parsed():
|
||||
fmt.Printf("Show version\n\n")
|
||||
s.versionCmd.PrintDefaults()
|
||||
os.Exit(0)
|
||||
default:
|
||||
s.usage()
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
if s.configPathFlag == "" {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to find user home directory, %v", err)
|
||||
}
|
||||
|
||||
configDirPath := fmt.Sprintf("%s/.nodebalancer", homeDir)
|
||||
configPath := fmt.Sprintf("%s/config.txt", configDirPath)
|
||||
|
||||
err = os.MkdirAll(configDirPath, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to create directory, %v", err)
|
||||
}
|
||||
|
||||
_, err = os.Stat(configPath)
|
||||
if err != nil {
|
||||
tempConfigB := []byte("ethereum,http://127.0.0.1,8545")
|
||||
err = os.WriteFile(configPath, tempConfigB, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to write config, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
s.configPathFlag = configPath
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StateCLI) populateCLI() {
|
||||
// Subcommands setup
|
||||
s.serverCmd = flag.NewFlagSet("server", flag.ExitOnError)
|
||||
s.versionCmd = flag.NewFlagSet("version", flag.ExitOnError)
|
||||
|
||||
// Common flag pointers
|
||||
for _, fs := range []*flag.FlagSet{s.serverCmd, s.versionCmd} {
|
||||
fs.BoolVar(&s.helpFlag, "help", false, "Show help message")
|
||||
fs.StringVar(&s.configPathFlag, "config", "", "Path to configuration file (default: ~/.nodebalancer/config.txt)")
|
||||
}
|
||||
|
||||
// Server subcommand flag pointers
|
||||
s.serverCmd.StringVar(&s.listeningAddrFlag, "host", "127.0.0.1", "Server listening address")
|
||||
s.serverCmd.StringVar(&s.listeningPortFlag, "port", "8544", "Server listening port")
|
||||
s.serverCmd.BoolVar(&s.enableHealthCheckFlag, "healthcheck", false, "To enable healthcheck ser healthcheck flag")
|
||||
s.serverCmd.BoolVar(&s.enableDebugFlag, "debug", false, "To enable debug mode with extended log set debug flag")
|
||||
}
|
||||
|
||||
func CLI() {
|
||||
stateCLI.populateCLI()
|
||||
if len(os.Args) < 2 {
|
||||
stateCLI.usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Parse subcommands and appropriate FlagSet
|
||||
switch os.Args[1] {
|
||||
case "server":
|
||||
stateCLI.serverCmd.Parse(os.Args[2:])
|
||||
stateCLI.checkRequirements()
|
||||
|
||||
Server()
|
||||
|
||||
case "version":
|
||||
stateCLI.versionCmd.Parse(os.Args[2:])
|
||||
stateCLI.checkRequirements()
|
||||
|
||||
fmt.Printf("v%s\n", configs.NB_VERSION)
|
||||
|
||||
default:
|
||||
stateCLI.usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -8,13 +8,17 @@ import (
|
|||
configs "github.com/bugout-dev/moonstream/nodes/node_balancer/configs"
|
||||
)
|
||||
|
||||
var ethereumClientPool ClientPool
|
||||
var polygonClientPool ClientPool
|
||||
var (
|
||||
ethereumClientPool ClientPool
|
||||
polygonClientPool ClientPool
|
||||
xdaiClientPool ClientPool
|
||||
)
|
||||
|
||||
// Generate client pools for different blockchains
|
||||
func CreateClientPools() {
|
||||
ethereumClientPool.Client = make(map[string]*Client)
|
||||
polygonClientPool.Client = make(map[string]*Client)
|
||||
xdaiClientPool.Client = make(map[string]*Client)
|
||||
}
|
||||
|
||||
// Return client pool correspongin to blockchain
|
||||
|
@ -24,6 +28,8 @@ func GetClientPool(blockchain string) (*ClientPool, error) {
|
|||
cpool = ðereumClientPool
|
||||
} else if blockchain == "polygon" {
|
||||
cpool = &polygonClientPool
|
||||
} else if blockchain == "xdai" {
|
||||
cpool = &xdaiClientPool
|
||||
} else {
|
||||
return nil, errors.New("Unexisting blockchain provided")
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@ func lbHandler(w http.ResponseWriter, r *http.Request) {
|
|||
blockchain = "ethereum"
|
||||
case strings.HasPrefix(r.URL.Path, "/nb/polygon"):
|
||||
blockchain = "polygon"
|
||||
case strings.HasPrefix(r.URL.Path, "/nb/xdai"):
|
||||
blockchain = "xdai"
|
||||
default:
|
||||
http.Error(w, fmt.Sprintf("Unacceptable blockchain provided %s", blockchain), http.StatusBadRequest)
|
||||
return
|
||||
|
|
|
@ -5,7 +5,6 @@ package cmd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
@ -29,7 +28,8 @@ func initHealthCheck(debug bool) {
|
|||
blockchainPool.HealthCheck()
|
||||
ethereumClients := ethereumClientPool.CleanInactiveClientNodes()
|
||||
polygonClients := polygonClientPool.CleanInactiveClientNodes()
|
||||
log.Printf("Active etehereum clients: %d, polygon clients: %d\n", ethereumClients, polygonClients)
|
||||
xdaiClients := xdaiClientPool.CleanInactiveClientNodes()
|
||||
log.Printf("Active etehereum clients: %d, polygon clients: %d, xdai clients: %d\n", ethereumClients, polygonClients, xdaiClients)
|
||||
if debug {
|
||||
blockchainPool.StatusLog()
|
||||
}
|
||||
|
@ -92,24 +92,7 @@ func proxyErrorHandler(proxy *httputil.ReverseProxy, url *url.URL) {
|
|||
}
|
||||
}
|
||||
|
||||
func InitServer() {
|
||||
var listeningAddr string
|
||||
var listeningPort string
|
||||
var enableHealthCheck bool
|
||||
var enableDebug bool
|
||||
var showVersion bool
|
||||
flag.StringVar(&listeningAddr, "host", "127.0.0.1", "Server listening address")
|
||||
flag.StringVar(&listeningPort, "port", "8544", "Server listening port")
|
||||
flag.BoolVar(&enableHealthCheck, "healthcheck", false, "To enable healthcheck ser healthcheck flag")
|
||||
flag.BoolVar(&enableDebug, "debug", false, "To enable debug mode with extended log set debug flag")
|
||||
flag.BoolVar(&showVersion, "version", false, "Print version")
|
||||
flag.Parse()
|
||||
|
||||
if showVersion {
|
||||
fmt.Printf("Node balancer version: v%s\n", configs.NODE_BALANCER_VERSION)
|
||||
return
|
||||
}
|
||||
|
||||
func Server() {
|
||||
// Generate map of clients
|
||||
CreateClientPools()
|
||||
|
||||
|
@ -125,7 +108,7 @@ func InitServer() {
|
|||
reporter.Publish(humbug.SystemReport())
|
||||
|
||||
// Fill NodeConfigList with initial nodes from environment variables
|
||||
configs.ConfigList.InitNodeConfigList()
|
||||
configs.ConfigList.InitNodeConfigList(stateCLI.configPathFlag)
|
||||
|
||||
// Parse nodes and set list of proxies
|
||||
for i, nodeConfig := range configs.ConfigList.Configs {
|
||||
|
@ -165,18 +148,18 @@ func InitServer() {
|
|||
commonHandler = panicMiddleware(commonHandler)
|
||||
|
||||
server := http.Server{
|
||||
Addr: fmt.Sprintf("%s:%s", listeningAddr, listeningPort),
|
||||
Addr: fmt.Sprintf("%s:%s", stateCLI.listeningAddrFlag, stateCLI.listeningPortFlag),
|
||||
Handler: commonHandler,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
// Start node health checking and current block fetching
|
||||
if enableHealthCheck {
|
||||
go initHealthCheck(enableDebug)
|
||||
if stateCLI.enableHealthCheckFlag {
|
||||
go initHealthCheck(stateCLI.enableDebugFlag)
|
||||
}
|
||||
|
||||
log.Printf("Starting server at %s:%s\n", listeningAddr, listeningPort)
|
||||
log.Printf("Starting server at %s:%s\n", stateCLI.listeningAddrFlag, stateCLI.listeningPortFlag)
|
||||
err = server.ListenAndServe()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
|
@ -4,9 +4,11 @@ Configurations for load balancer server.
|
|||
package configs
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -28,67 +30,43 @@ type NodeConfigList struct {
|
|||
|
||||
var ConfigList NodeConfigList
|
||||
|
||||
var MOONSTREAM_NODE_ETHEREUM_A_IPC_ADDR = os.Getenv("MOONSTREAM_NODE_ETHEREUM_A_IPC_ADDR")
|
||||
var MOONSTREAM_NODE_ETHEREUM_B_IPC_ADDR = os.Getenv("MOONSTREAM_NODE_ETHEREUM_B_IPC_ADDR")
|
||||
var MOONSTREAM_NODE_ETHEREUM_IPC_PORT = os.Getenv("MOONSTREAM_NODE_ETHEREUM_IPC_PORT")
|
||||
var MOONSTREAM_NODE_POLYGON_A_IPC_ADDR = os.Getenv("MOONSTREAM_NODE_POLYGON_A_IPC_ADDR")
|
||||
var MOONSTREAM_NODE_POLYGON_B_IPC_ADDR = os.Getenv("MOONSTREAM_NODE_POLYGON_B_IPC_ADDR")
|
||||
var MOONSTREAM_NODE_POLYGON_IPC_PORT = os.Getenv("MOONSTREAM_NODE_POLYGON_IPC_PORT")
|
||||
var MOONSTREAM_NODES_SERVER_PORT = os.Getenv("MOONSTREAM_NODES_SERVER_PORT")
|
||||
var MOONSTREAM_CLIENT_ID_HEADER = os.Getenv("MOONSTREAM_CLIENT_ID_HEADER")
|
||||
|
||||
func checkEnvVarSet() {
|
||||
if MOONSTREAM_NODE_ETHEREUM_A_IPC_ADDR == "" {
|
||||
MOONSTREAM_NODE_ETHEREUM_A_IPC_ADDR = "a.ethereum.moonstream.internal"
|
||||
}
|
||||
if MOONSTREAM_NODE_ETHEREUM_B_IPC_ADDR == "" {
|
||||
MOONSTREAM_NODE_ETHEREUM_B_IPC_ADDR = "b.ethereum.moonstream.internal"
|
||||
}
|
||||
|
||||
if MOONSTREAM_NODE_POLYGON_A_IPC_ADDR == "" {
|
||||
MOONSTREAM_NODE_POLYGON_A_IPC_ADDR = "a.polygon.moonstream.internal"
|
||||
}
|
||||
if MOONSTREAM_NODE_POLYGON_B_IPC_ADDR == "" {
|
||||
MOONSTREAM_NODE_POLYGON_B_IPC_ADDR = "b.polygon.moonstream.internal"
|
||||
}
|
||||
|
||||
if MOONSTREAM_CLIENT_ID_HEADER == "" {
|
||||
MOONSTREAM_CLIENT_ID_HEADER = "x-moonstream-client-id"
|
||||
}
|
||||
|
||||
if MOONSTREAM_NODES_SERVER_PORT == "" || MOONSTREAM_NODE_ETHEREUM_IPC_PORT == "" || MOONSTREAM_NODE_POLYGON_IPC_PORT == "" {
|
||||
log.Fatal("Some of environment variables not set")
|
||||
if MOONSTREAM_NODES_SERVER_PORT == "" {
|
||||
log.Fatal("Environment variable MOONSTREAM_NODES_SERVER_PORT not set")
|
||||
}
|
||||
}
|
||||
|
||||
// Return list of NodeConfig structures
|
||||
func (nc *NodeConfigList) InitNodeConfigList() {
|
||||
func (nc *NodeConfigList) InitNodeConfigList(configPath string) {
|
||||
checkEnvVarSet()
|
||||
|
||||
// Define available blockchain nodes
|
||||
blockchainConfigList := make([]BlockchainConfig, 0, 2)
|
||||
blockchainConfigList = append(blockchainConfigList, BlockchainConfig{
|
||||
Blockchain: "ethereum",
|
||||
IPs: []string{MOONSTREAM_NODE_ETHEREUM_A_IPC_ADDR, MOONSTREAM_NODE_ETHEREUM_B_IPC_ADDR},
|
||||
Port: MOONSTREAM_NODE_ETHEREUM_IPC_PORT,
|
||||
})
|
||||
blockchainConfigList = append(blockchainConfigList, BlockchainConfig{
|
||||
Blockchain: "polygon",
|
||||
IPs: []string{MOONSTREAM_NODE_POLYGON_A_IPC_ADDR, MOONSTREAM_NODE_POLYGON_B_IPC_ADDR},
|
||||
Port: MOONSTREAM_NODE_POLYGON_IPC_PORT,
|
||||
})
|
||||
rawBytes, err := ioutil.ReadFile(configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to read config file, %v", err)
|
||||
}
|
||||
text := string(rawBytes)
|
||||
lines := strings.Split(text, "\n")
|
||||
|
||||
// Parse node addr, ip and blockchain
|
||||
for _, b := range blockchainConfigList {
|
||||
for _, nodeIP := range b.IPs {
|
||||
port, err := strconv.ParseInt(b.Port, 0, 16)
|
||||
// Define available blockchain nodes
|
||||
for _, line := range lines {
|
||||
fields := strings.Split(line, ",")
|
||||
if len(fields) == 3 {
|
||||
port, err := strconv.ParseInt(fields[2], 0, 16)
|
||||
if err != nil {
|
||||
log.Printf("Unable to parse port number: %s", b.Port)
|
||||
log.Printf("Unable to parse port number, %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
nc.Configs = append(nc.Configs, NodeConfig{
|
||||
Blockchain: b.Blockchain,
|
||||
Addr: nodeIP,
|
||||
Blockchain: fields[0],
|
||||
Addr: fields[1],
|
||||
Port: uint16(port),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package configs
|
||||
|
||||
var NODE_BALANCER_VERSION = "0.0.1"
|
||||
var NB_VERSION = "0.0.2"
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
# Compile application and run with provided arguments
|
||||
set -e
|
||||
|
||||
PROGRAM_NAME="nodebalancer"
|
||||
|
||||
go build -o "$PROGRAM_NAME" .
|
||||
|
||||
./"$PROGRAM_NAME" "$@"
|
|
@ -5,5 +5,5 @@ import (
|
|||
)
|
||||
|
||||
func main() {
|
||||
cmd.InitServer()
|
||||
cmd.CLI()
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ func (es *extendedServer) pingGethRoute(w http.ResponseWriter, req *http.Request
|
|||
http.Error(w, http.StatusText(500), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if es.blockchain != "ethereum" && es.blockchain != "polygon" {
|
||||
if es.blockchain != "ethereum" && es.blockchain != "polygon" && es.blockchain != "xdai" {
|
||||
log.Printf("Unaccepted blockchain type: %s", es.blockchain)
|
||||
http.Error(w, http.StatusText(400), http.StatusBadRequest)
|
||||
return
|
||||
|
|
|
@ -17,7 +17,7 @@ func InitServer() {
|
|||
var blockchain string
|
||||
flag.StringVar(&listeningAddr, "host", "127.0.0.1", "Server listening address")
|
||||
flag.StringVar(&listeningPort, "port", "8080", "Server listening port")
|
||||
flag.StringVar(&blockchain, "blockchain", "", "Blockchain to work with (Ethereum/Polygon)")
|
||||
flag.StringVar(&blockchain, "blockchain", "", "Blockchain to work with (ethereum/polygon/xdai)")
|
||||
flag.Parse()
|
||||
|
||||
es := extendedServer{blockchain: blockchain}
|
||||
|
|
Ładowanie…
Reference in New Issue