Skip to content

Commit f6d442b

Browse files
authored
Merge pull request #159 from celenium-io/dev
Release v1.20..0
2 parents 2c54c7d + 560c1b1 commit f6d442b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+5908
-5117
lines changed

.dockerignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
node_modules/
2+
.nuxt/
3+
.output/
4+
dist/
5+
6+
pnpm-lock.yaml
7+
8+
.git/
9+
.gitignore
10+
11+
.dockerignore
12+
Dockerfile

Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM node:20
2+
3+
WORKDIR /app
4+
5+
COPY . .
6+
7+
ENV NODE_OPTIONS="--max-old-space-size=4096"
8+
9+
RUN npm install --legacy-peer-deps
10+
RUN npm run build
11+
12+
CMD ["npm", "run", "start"]

README.md

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ Clone the repository and run on the root folder:
1111
pnpm i
1212
pnpm dev
1313
```
14-
1514
### Node.js Server
1615

1716
When running `nuxt build` with the Node server preset, the result will be an entry point that launches a ready-to-run Node server.
@@ -41,3 +40,57 @@ module.exports = {
4140
Also, you can use different [presets](https://nuxt.com/docs/getting-started/deployment#hosting-providers). E.g. **Cloudflare Pages**: `cloudflare_pages`.
4241

4342
Note, some providers do not support server-side rendering.
43+
44+
---
45+
46+
### Docker Setup
47+
48+
You can also run the application in Docker.
49+
Build the image and run the container:
50+
```
51+
docker build -t celenium-app
52+
docker run -p 3000:3000 --env-file .env celenium-app
53+
```
54+
Make sure to create a ```.env``` file in the root directory or pass the required environment variables directly with ```-e```.
55+
56+
### Run with Docker Compose
57+
Start with:
58+
```
59+
docker-compose up -d
60+
```
61+
62+
By default:
63+
- Builds the image from the local `Dockerfile`
64+
- Runs the app on `127.0.0.1:3000`
65+
- Automatically restarts the container on failure
66+
- Uses `npm run start` as the startup command
67+
- Limits logs (10 MB per file, max 5 files)
68+
69+
If you want to use a prebuilt image from **GitHub Container Registry**, specify a tag:
70+
- `TAG=latest docker-compose up -d`
71+
72+
---
73+
74+
### Environment Variables
75+
76+
#### Required for App Startup
77+
- **NUXT_PUBLIC_API_DEV** — indexer API (e.g. `https://api.localhost:9876/v1`).
78+
- **NUXT_PUBLIC_WSS_DEV** — webSocket endpoint (e.g. `wss://api.localhost:9876/v1/ws`).
79+
- **NUXT_PUBLIC_SELFHOSTED** — set to `true` when running in self-hosted mode.
80+
81+
#### Blobstream Configuration
82+
- **NUXT_PUBLIC_BLOBSTREAM_MAINNET** — API for blobstream data.
83+
84+
#### Faucet Configuration
85+
- **NUXT_PUBLIC_FAUCET_ADDRESS** — faucet address.
86+
- **NUXT_PUBLIC_FAUCET_MOCHA** — faucet API for the Mocha network.
87+
- **NUXT_PUBLIC_FAUCET_ARABICA** — faucet API for the Arabica network.
88+
- **NUXT_PUBLIC_FAUCET_MAMMOTH** — faucet API for the Mammoth network.
89+
90+
#### External Services Configuration
91+
- **NUXT_PUBLIC_BLOCKSCOUT** — used to check whether a batch exists in Blockscout. If found, a dedicated button will appear on the blob form/page.
92+
- **NUXT_PUBLIC_NODE_STATS** — provides statistics about node types, versions, and geographic distribution across the Celestia ecosystem.
93+
- **NUXT_PUBLIC_QUOTE** — provides price data. It is used to display the current TIA price in the header and to convert all values from TIA to USD.
94+
- **NUXT_PUBLIC_ROLLUP_RANKING** — fetches rollup ranking data displayed on the rollup leaderboard, individual rollup pages, and a dedicated rollup ranking page. The ranking page also includes detailed calculations, as well as repository and commit statistics.
95+
- **NUXT_PUBLIC_GITHUB** — required for retrieving repository statistics on a rollup ranking page.
96+
- **NUXT_PUBLIC_TVL** — provides TVL (Total Value Locked) statistics for rollups and TVS (Total Value Secured) for the Celestia network. These values are displayed in the header, on the statistics page, and on individual rollup pages.

app.vue

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,13 @@ import { useSettingsStore } from "@/store/settings.store"
2424
import { useEnumStore } from "@/store/enums.store"
2525
import { useLegalStore } from "@/store/legal.store"
2626
import { useNotificationsStore } from "@/store/notifications.store"
27-
import { useActivityStore } from "@/store/activity.store"
2827
const nodeStore = useNodeStore()
2928
const appStore = useAppStore()
3029
const bookmarksStore = useBookmarksStore()
3130
const settingsStore = useSettingsStore()
3231
const enumStore = useEnumStore()
3332
const legalStore = useLegalStore()
3433
const notificationsStore = useNotificationsStore()
35-
const activityStore = useActivityStore()
3634
3735
bookmarksStore.$subscribe((mutation, state) => {
3836
localStorage.setItem("bookmarks", JSON.stringify(state.bookmarks))
@@ -43,9 +41,6 @@ settingsStore.$subscribe((mutation, state) => {
4341
legalStore.$subscribe((mutation, state) => {
4442
localStorage.setItem("legal", JSON.stringify(state.legal))
4543
})
46-
activityStore.$subscribe((mutation, state) => {
47-
localStorage.setItem("rollups_ranking", JSON.stringify(state.rollups_ranking))
48-
})
4944
5045
appStore.initConstants()
5146
@@ -98,8 +93,9 @@ onMounted(async () => {
9893
}
9994
10095
settingsStore.init()
101-
activityStore.init()
10296
97+
appStore.initGlobalUpdates()
98+
10399
const runtimeConfig = useRuntimeConfig()
104100
amp.init(runtimeConfig.public.AMP)
105101

assets/icons.json

Lines changed: 13 additions & 0 deletions
Large diffs are not rendered by default.

assets/styles/base.scss

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ $grayscale: (
7878
--block-progress-fill-background: #33a853;
7979
--logo-name: var(--txt-primary);
8080
--bar-fill: rgb(243, 147, 45);
81-
--validator-active: #85f891;
81+
--validator-active: #18d2a5;
8282
--validator-inactive: #1ca7ed;
8383
--validator-jailed: #f8774a;
8484
--supply: #1ca7ed;
@@ -378,6 +378,21 @@ body {
378378
transform: translateY(-25px);
379379
}
380380

381+
.slide-fade-enter-active,
382+
.slide-fade-leave-active {
383+
transition: all 0.2s ease;
384+
position: absolute;
385+
width: 100%;
386+
}
387+
.slide-fade-enter-from {
388+
opacity: 0;
389+
transform: translateX(-30%);
390+
}
391+
.slide-fade-leave-to {
392+
opacity: 0;
393+
transform: translateX(30%);
394+
}
395+
381396
.table_column_alias {
382397
max-width: 125px;
383398

components/Feed.vue

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,26 @@
33
import { DateTime } from "luxon"
44
55
/** Services */
6-
import { abbreviate, comma, formatBytes, isMainnet, roundTo } from "@/services/utils"
6+
import { abbreviate, capitilize, comma, formatBytes, isMainnet, roundTo } from "@/services/utils"
77
import { getRankCategory } from "@/services/constants/rollups"
8+
import { quoteServiceURL, rollupRankingServiceURL } from "@/services/config"
89
910
/** UI */
1011
import Tooltip from "@/components/ui/Tooltip.vue"
1112
1213
/** API */
14+
import { fetchRollupsRanking } from "@/services/api/rollup"
1315
import { fetchPriceSeries, fetchSummary, fetchTVS } from "@/services/api/stats"
1416
1517
/** Store */
1618
import { useAppStore } from "@/store/app.store"
17-
import { useActivityStore } from "@/store/activity.store"
1819
const appStore = useAppStore()
19-
const activityStore = useActivityStore()
2020
2121
const head = computed(() => appStore.lastHead)
2222
const currentPrice = computed(() => appStore.currentPrice)
2323
2424
const totalFees = computed(() => head.value.total_fee / 1_000_000)
2525
const totalFeesUSD = computed(() => totalFees.value * currentPrice.value?.close)
26-
const topRollup = computed(() => {
27-
let rankCategory = getRankCategory(roundTo(activityStore?.rollups_ranking?.top_rollup?.rank / 10, 0))
28-
return {
29-
slug: activityStore?.rollups_ranking?.top_rollup?.slug,
30-
name: activityStore?.rollups_ranking?.top_rollup?.name,
31-
rank: {
32-
name: rankCategory?.name,
33-
score: activityStore?.rollups_ranking?.top_rollup?.rank,
34-
color: rankCategory?.color,
35-
},
36-
}
37-
})
3826
3927
const isLoading = ref(true)
4028
const series = ref([])
@@ -43,24 +31,43 @@ const price = reactive({
4331
diff: 0,
4432
side: null,
4533
})
34+
const showPrice = ref(!!quoteServiceURL())
35+
const topRollup = ref(null)
36+
const showTopRollup = ref(isMainnet() && !!rollupRankingServiceURL())
4637
const tvs = computed(() => appStore.tvs)
4738
const txCount24h = ref(0)
4839
const bytesInBlocks24h = ref(0)
4940
5041
onMounted(async () => {
51-
const dataSeries = await fetchPriceSeries({ from: parseInt(DateTime.now().minus({ days: 3 }).ts / 1_000) })
52-
series.value = dataSeries
53-
appStore.currentPrice = series.value[0]
54-
price.value = parseFloat(series.value[0].close)
55-
56-
const prevDayClosePrice = parseFloat(series.value[1].close)
57-
price.diff = (Math.abs(prevDayClosePrice - price.value) / ((prevDayClosePrice + price.value) / 2)) * 100
58-
let side = "stay"
59-
if (price.value - prevDayClosePrice !== 0) {
60-
side = price.value - prevDayClosePrice > 0 ? "rise" : "fall"
42+
if (showPrice.value) {
43+
const dataSeries = await fetchPriceSeries({ from: parseInt(DateTime.now().minus({ days: 3 }).ts / 1_000) })
44+
if (dataSeries.length) {
45+
series.value = dataSeries
46+
appStore.currentPrice = series.value[0]
47+
price.value = parseFloat(series.value[0].close)
48+
49+
const prevDayClosePrice = parseFloat(series.value[1].close)
50+
price.diff = (Math.abs(prevDayClosePrice - price.value) / ((prevDayClosePrice + price.value) / 2)) * 100
51+
let side = "stay"
52+
if (price.value - prevDayClosePrice !== 0) {
53+
side = price.value - prevDayClosePrice > 0 ? "rise" : "fall"
54+
}
55+
price.side = side
56+
}
6157
}
62-
price.side = side
6358
59+
if (showTopRollup.value) {
60+
const _topRollups = await fetchRollupsRanking({ limit: 1 })
61+
if (_topRollups.length) {
62+
const _r = _topRollups[0]
63+
topRollup.value = {
64+
..._r,
65+
category: getRankCategory(roundTo(_r.rank / 10, 0)),
66+
name: _r.slug.split("-").map(el => capitilize(el)).join(" "),
67+
}
68+
}
69+
}
70+
6471
const _tvs = await fetchTVS({ period: null })
6572
if (_tvs.value) {
6673
appStore.tvs = _tvs.value
@@ -83,15 +90,15 @@ onMounted(async () => {
8390
<Flex tag="section" justify="center" wide :class="$style.wrapper">
8491
<Flex align="center" justify="between" gap="24" wide :class="$style.container">
8592
<Flex align="center" gap="12" :class="$style.stats">
86-
<template v-if="isMainnet()">
93+
<template v-if="showTopRollup">
8794
<NuxtLink :to="`/rollup/rank/${topRollup?.slug}`">
8895
<Tooltip>
8996
<Flex align="center" gap="6" :class="$style.stat">
9097
<Icon
9198
v-if="topRollup?.name"
9299
name="laurel"
93100
size="14"
94-
:color="topRollup?.rank?.color"
101+
:color="topRollup?.category?.color"
95102
:style="{ marginTop: '1px' }"
96103
/>
97104
<Icon v-else name="laurel" size="14" color="tertiary" :class="$style.icon" :style="{ marginTop: '1px' }" />
@@ -109,11 +116,11 @@ onMounted(async () => {
109116
<Flex direction="column" gap="8">
110117
<Flex align="center" justify="between" gap="8">
111118
<Text size="12" weight="500" color="tertiary">Rank:</Text>
112-
<Text size="12" weight="600" :color="topRollup?.rank?.color"> {{ topRollup?.rank?.name }} </Text>
119+
<Text size="12" weight="600" :color="topRollup?.category?.color"> {{ topRollup?.category?.name }} </Text>
113120
</Flex>
114121
<Flex align="center" justify="between" gap="8">
115122
<Text size="12" weight="500" color="tertiary">Score:</Text>
116-
<Text size="12" weight="600" color="secondary"> {{ topRollup?.rank?.score }}% </Text>
123+
<Text size="12" weight="600" color="secondary"> {{ topRollup?.rank }}% </Text>
117124
</Flex>
118125
</Flex>
119126
</template>
@@ -191,7 +198,7 @@ onMounted(async () => {
191198
</Tooltip>
192199
</Flex>
193200
194-
<Tooltip position="end">
201+
<Tooltip v-if="showPrice" position="end">
195202
<Flex align="center" gap="6" :class="$style.stat">
196203
<Icon name="coin" size="12" color="secondary" :class="$style.icon" />
197204

components/LeftSidebar.vue

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import NavLink from "@/components/modules/navigation/NavLink.vue"
1111
import { getNetworkName } from "@/services/utils/general"
1212
import { StatusMap } from "@/services/constants/node"
1313
import { isMainnet, isMobile } from "@/services/utils"
14+
import { nodeStatsURL } from "@/services/config"
15+
import { isSelfhosted } from "@/services/config.js"
1416
1517
/** Store */
1618
import { useAppStore } from "@/store/app.store"
@@ -95,7 +97,7 @@ const mainLinks = reactive([
9597
name: "Nodes",
9698
path: "/stats?tab=nodes",
9799
queryParam: { tab: "nodes" },
98-
show: isMainnet(),
100+
show: isMainnet() && !!nodeStatsURL(),
99101
},
100102
],
101103
},
@@ -126,6 +128,18 @@ const modularLinks = reactive([
126128
},
127129
],
128130
},
131+
{
132+
icon: "hyperlane",
133+
name: "Interop",
134+
path: "/hyperlane",
135+
children: [
136+
{
137+
name: "Transfers",
138+
path: "/hyperlane/transfers",
139+
show: true,
140+
},
141+
],
142+
},
129143
{
130144
icon: "ibc",
131145
name: "IBC",
@@ -164,12 +178,18 @@ const modularLinks = reactive([
164178
165179
const isToolsLinkCollapsed = ref(false)
166180
const toolsLinks = reactive([
181+
{
182+
icon: "widgets",
183+
name: "Widgets",
184+
path: "https://widgets.celenium.io",
185+
external: true,
186+
new: true,
187+
},
167188
{
168189
icon: "explorable",
169190
name: "Terminal",
170191
path: "https://terminal.celenium.io",
171192
external: true,
172-
new: true,
173193
},
174194
{
175195
icon: "drop",
@@ -294,7 +314,7 @@ const handleOnClose = () => {
294314
<Text v-else size="12" weight="600" color="tertiary">{{ nodeStore.percentage.toFixed(0) }}%</Text>
295315
</Flex>
296316

297-
<Dropdown position="end" fullWidth>
317+
<Dropdown position="end" fullWidth :disabled="isSelfhosted()">
298318
<Flex align="center" gap="8" justify="between" :class="$style.network_selector">
299319
<Flex align="center" gap="8">
300320
<Icon name="globe" size="14" :color="head.synced ? 'brand' : 'red'" />

components/cmd/CommandMenu.vue

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,15 @@ const rawNavigationActions = [
238238
router.push("/rollups")
239239
},
240240
},
241+
{
242+
type: "callback",
243+
icon: "arrow-narrow-right",
244+
title: "Go to Hyperlane",
245+
runText: "Open Hyperlane",
246+
callback: () => {
247+
router.push("/hyperlane")
248+
},
249+
},
241250
{
242251
type: "callback",
243252
icon: "arrow-narrow-right",

0 commit comments

Comments
 (0)