Skip to content

Commit 083fef7

Browse files
committed
Merge branch 'deployment' into dev
2 parents 9205fdf + 710a365 commit 083fef7

29 files changed

Lines changed: 2104 additions & 2064 deletions

.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

.github/workflows/docker-build.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Build and Push Docker Image
2+
3+
on:
4+
workflow_dispatch:
5+
# on:
6+
# push:
7+
# tags:
8+
# - 'v*'
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Checkout repository
16+
uses: actions/checkout@v4
17+
18+
- name: Log in to GitHub Container Registry
19+
uses: docker/login-action@v3
20+
with:
21+
registry: ghcr.io
22+
username: ${{ github.actor }}
23+
password: ${{ secrets.GITHUB_TOKEN }}
24+
25+
- name: Build Docker Image
26+
run: |
27+
docker build -t ghcr.io/${{ github.repository }}:${{ github.ref_name }} .
28+
29+
- name: Push Docker Image to GitHub Container Registry
30+
run: |
31+
docker push ghcr.io/${{ github.repository }}:${{ github.ref_name }}

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.

components/Feed.vue

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { DateTime } from "luxon"
55
/** Services */
66
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"
@@ -30,36 +31,43 @@ const price = reactive({
3031
diff: 0,
3132
side: null,
3233
})
34+
const showPrice = ref(!!quoteServiceURL())
3335
const topRollup = ref(null)
36+
const showTopRollup = ref(isMainnet() && !!rollupRankingServiceURL())
3437
const tvs = computed(() => appStore.tvs)
3538
const txCount24h = ref(0)
3639
const bytesInBlocks24h = ref(0)
3740
3841
onMounted(async () => {
39-
const dataSeries = await fetchPriceSeries({ from: parseInt(DateTime.now().minus({ days: 3 }).ts / 1_000) })
40-
series.value = dataSeries
41-
appStore.currentPrice = series.value[0]
42-
price.value = parseFloat(series.value[0].close)
43-
44-
const prevDayClosePrice = parseFloat(series.value[1].close)
45-
price.diff = (Math.abs(prevDayClosePrice - price.value) / ((prevDayClosePrice + price.value) / 2)) * 100
46-
let side = "stay"
47-
if (price.value - prevDayClosePrice !== 0) {
48-
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+
}
4957
}
50-
price.side = side
51-
52-
const _topRollups = await fetchRollupsRanking({ limit: 1 })
53-
if (_topRollups.length) {
54-
const _r = _topRollups[0]
55-
topRollup.value = {
56-
..._r,
57-
category: getRankCategory(roundTo(_r.rank / 10, 0)),
58-
name: _r.slug.split("-").map(el => capitilize(el)).join(" "),
58+
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+
}
5968
}
6069
}
6170
62-
6371
const _tvs = await fetchTVS({ period: null })
6472
if (_tvs.value) {
6573
appStore.tvs = _tvs.value
@@ -82,7 +90,7 @@ onMounted(async () => {
8290
<Flex tag="section" justify="center" wide :class="$style.wrapper">
8391
<Flex align="center" justify="between" gap="24" wide :class="$style.container">
8492
<Flex align="center" gap="12" :class="$style.stats">
85-
<template v-if="isMainnet()">
93+
<template v-if="showTopRollup">
8694
<NuxtLink :to="`/rollup/rank/${topRollup?.slug}`">
8795
<Tooltip>
8896
<Flex align="center" gap="6" :class="$style.stat">
@@ -190,7 +198,7 @@ onMounted(async () => {
190198
</Tooltip>
191199
</Flex>
192200
193-
<Tooltip position="end">
201+
<Tooltip v-if="showPrice" position="end">
194202
<Flex align="center" gap="6" :class="$style.stat">
195203
<Icon name="coin" size="12" color="secondary" :class="$style.icon" />
196204

components/LeftSidebar.vue

Lines changed: 4 additions & 2 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
},
@@ -300,7 +302,7 @@ const handleOnClose = () => {
300302
<Text v-else size="12" weight="600" color="tertiary">{{ nodeStore.percentage.toFixed(0) }}%</Text>
301303
</Flex>
302304

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

components/modals/BlobModal.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ const handlePreviewContent = () => {
214214
215215
<Flex direction="column" align="center" gap="8">
216216
<Text size="13" weight="600" color="secondary">Download not started</Text>
217-
<Text size="12" weight="500" color="tertiary">Auto download for data over 1 Mb is paused</Text>
217+
<Text size="12" weight="500" color="tertiary">Auto download for data over 1 MiB is paused</Text>
218218
</Flex>
219219
220220
<Text @click="handleLoadAnyway" size="12" weight="600" color="tertiary" :class="$style.load_btn"

components/modules/ibc/NotableStats.vue

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@ props.ibcData.rawChainsStats.forEach((chainStats) => {
2929
largestChain.value = chainStats
3030
}
3131
})
32-
const largestTransfer = props.ibcData.rawSummary.largest_transfer
33-
const busiestChannel = props.ibcData.rawSummary.busiest_channel
32+
const largestTransfer = props.ibcData.rawSummary?.largest_transfer
33+
const busiestChannel = props.ibcData.rawSummary?.busiest_channel
3434
3535
const handleOpenTransferModal = (transfer) => {
3636
cacheStore.current.transfer = transfer
3737
modalsStore.open("ibcTransfer")
3838
}
3939
4040
const getChainName = (target) => {
41-
return largestTransfer[target].hash.startsWith("celestia")
41+
return largestTransfer[target]?.hash?.startsWith("celestia")
4242
? "Celestia"
43-
: IbcChainName[largestTransfer.chain_id] ?? largestTransfer.chain_id
43+
: IbcChainName[largestTransfer?.chain_id] ?? largestTransfer?.chain_id
4444
}
4545
</script>
4646
@@ -67,7 +67,7 @@ const getChainName = (target) => {
6767
<template #content> The amount shown is the chain flow, sent and received tokens </template>
6868
</Tooltip>
6969
70-
<Flex @click="handleOpenTransferModal(largestTransfer)" wide direction="column" gap="12" :class="[$style.card, $style.hoverable]">
70+
<Flex v-if="largestTransfer" @click="handleOpenTransferModal(largestTransfer)" wide direction="column" gap="12" :class="[$style.card, $style.hoverable]">
7171
<Flex align="center" justify="between">
7272
<Flex align="center" gap="8">
7373
<Icon name="arrow-circle-broken-right" size="14" color="brand" />
@@ -97,12 +97,12 @@ const getChainName = (target) => {
9797
</Flex>
9898
9999
<Text size="15" weight="600" color="primary">
100-
{{ busiestChannel.channel_id }}
101-
<Text size="12" color="tertiary">{{ IbcChainName[busiestChannel.chain_id] ?? "Unknown" }}</Text>
100+
{{ busiestChannel ? busiestChannel.channel_id : "" }}
101+
<Text size="12" color="tertiary">{{ busiestChannel ? IbcChainName[busiestChannel.chain_id] ?? "Unknown" : "Unknown" }}</Text>
102102
</Text>
103103
104104
<Text size="13" weight="500" color="support">
105-
<Text color="tertiary" mono>{{ comma(busiestChannel.transfers_count) }} transfers</Text>
105+
<Text color="tertiary" mono>{{ comma(busiestChannel?.transfers_count ?? "") }} transfers</Text>
106106
</Text>
107107
</Flex>
108108
</Flex>

components/modules/rollup/RollupOverview.vue

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import NamespacesTable from "./tables/NamespacesTable.vue"
1818
import { capitilize, comma, formatBytes, hexToRgba, isMainnet, roundTo, truncateDecimalPart } from "@/services/utils"
1919
import { exportToCSV } from "@/services/utils/export"
2020
import { getRankCategory } from "@/services/constants/rollups"
21+
import { rollupRankingServiceURL } from "@/services/config"
2122
2223
/** API */
2324
import { fetchRollupBlobs, fetchRollupExportData, fetchRollupNamespaces, fetchRollupRankingBySlug } from "@/services/api/rollup"
@@ -61,6 +62,7 @@ const blobs = ref([])
6162
const rollupColor = ref()
6263
const rollupColorAlpha = ref()
6364
const rollupRanking = ref()
65+
const showRanking = ref(false)
6466
6567
const tagNames = ref(["stack", "type", "vm", "provider", "category"])
6668
const tags = computed(() =>
@@ -177,7 +179,9 @@ onMounted(async () => {
177179
rollupColor.value = hexToRgba(props.rollup.color, 1)
178180
rollupColorAlpha.value = hexToRgba(props.rollup.color, 0)
179181
180-
if (isMainnet()) {
182+
showRanking.value = isMainnet() && rollupRankingServiceURL()
183+
184+
if (showRanking.value) {
181185
const data = await fetchRollupRankingBySlug(props.rollup?.slug)
182186
if (data.slug) {
183187
rollupRanking.value = {
@@ -291,7 +295,7 @@ const handleCSVDownload = async (value) => {
291295
</Flex>
292296
293297
<Flex align="center" gap="12">
294-
<Button v-if="isMainnet()" :link="`/rollup/rank/${rollup.slug}`" type="secondary" size="mini">
298+
<Button v-if="showRanking" :link="`/rollup/rank/${rollup.slug}`" type="secondary" size="mini">
295299
<Icon name="laurel" size="12" color="secondary" />
296300
297301
<Text>Activity Rank</Text>
@@ -348,8 +352,7 @@ const handleCSVDownload = async (value) => {
348352
<img id="logo" :src="rollup.logo" :class="$style.rollup_logo" />
349353
</Flex>
350354
351-
<Flex v-if="isMainnet()" align="start" :style="{ height: '100%' }">
352-
<!-- <Tooltip position="end" :disabled="!rollupRanking?.rank?.category?.color"> -->
355+
<Flex v-if="showRanking" align="start" :style="{ height: '100%' }">
353356
<Tooltip position="end">
354357
<Icon
355358
name="laurel"
@@ -763,6 +766,7 @@ const handleCSVDownload = async (value) => {
763766
764767
text-overflow: ellipsis;
765768
overflow: hidden;
769+
line-clamp: 3;
766770
-webkit-line-clamp: 3;
767771
-webkit-box-orient: vertical;
768772
}

components/modules/stats/GeoMap.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ const buildChart = async (chart) => {
268268
let legendValues = []
269269
let legendGroup, legendCitiesMarkers, legendCitiesLabels
270270
if (!isMobile()) {
271-
if (chartView.value === "countries") {
271+
if (chartView.value === "countries" && countryMaxAmount) {
272272
legendGroup = svg
273273
.append("g")
274274
.attr("transform", `translate(${legend.marginLeft}, ${height - legend.marginBottom})`)

0 commit comments

Comments
 (0)