diff --git a/backend/package-lock.json b/backend/package-lock.json index b62274bb5e..f22907307c 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -17,8 +17,8 @@ "bip32": "^4.0.0", "bitcoinjs-lib": "~6.1.3", "crypto-js": "~4.2.0", - "express": "~4.21.1", "ecpair": "^2.1.0", + "express": "~4.21.1", "maxmind": "~4.3.11", "mysql2": "~3.11.0", "redis": "^4.7.0", diff --git a/backend/src/api/liquid/liquid.routes.ts b/backend/src/api/liquid/liquid.routes.ts index 9dafd0defd..c09e80dab4 100644 --- a/backend/src/api/liquid/liquid.routes.ts +++ b/backend/src/api/liquid/liquid.routes.ts @@ -12,6 +12,10 @@ class LiquidRoutes { .get(config.MEMPOOL.API_URL_PREFIX + 'assets/featured', this.$getAllFeaturedLiquidAssets) .get(config.MEMPOOL.API_URL_PREFIX + 'asset/:assetId/icon', this.getLiquidIcon) .get(config.MEMPOOL.API_URL_PREFIX + 'assets/group/:id', this.$getAssetGroup) + // ADD ANGOR ROUTES FOR LIQUID + .get(config.MEMPOOL.API_URL_PREFIX + 'angor/projects', this.$getAngorProjects) + .get(config.MEMPOOL.API_URL_PREFIX + 'angor/project/:id', this.$getAngorProject) + .get(config.MEMPOOL.API_URL_PREFIX + 'angor/stats', this.$getAngorStats) ; if (config.DATABASE.ENABLED) { @@ -35,6 +39,80 @@ class LiquidRoutes { } } + // ADD ANGOR METHODS + private async $getAngorProjects(req: Request, res: Response) { + try { + // Added some mock data, will get replaced by original ones when actual projects are present on op of liquid + const projects = [ + { + id: '1', + name: 'Liquid DeFi Platform', + description: 'Building a decentralized finance platform on Liquid Network', + fundingGoal: 100000000, + currentFunding: 45000000, + status: 'active', + createdAt: Date.now() - 30 * 24 * 60 * 60 * 1000, + endDate: Date.now() + 60 * 24 * 60 * 60 * 1000, + }, + { + id: '2', + name: 'Liquid NFT Marketplace', + description: 'A marketplace for NFTs on the Liquid sidechain', + fundingGoal: 50000000, + currentFunding: 50000000, + status: 'completed', + createdAt: Date.now() - 90 * 24 * 60 * 60 * 1000, + endDate: Date.now() - 10 * 24 * 60 * 60 * 1000, + } + ]; + + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 5).toUTCString()); + res.json(projects); + } catch (e) { + handleError(req, res, 500, e instanceof Error ? e.message : e); + } + } + + private async $getAngorProject(req: Request, res: Response) { + try { + const project = { + id: req.params.id, + name: 'Sample Project', + description: 'Sample project description', + fundingGoal: 100000000, + currentFunding: 45000000, + status: 'active', + createdAt: Date.now() - 30 * 24 * 60 * 60 * 1000, + endDate: Date.now() + 60 * 24 * 60 * 60 * 1000, + }; + + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 5).toUTCString()); + res.json(project); + } catch (e) { + handleError(req, res, 500, e instanceof Error ? e.message : e); + } + } + + private async $getAngorStats(req: Request, res: Response) { + try { + const stats = { + totalProjects: 2, + totalFunding: 95000000, + activeProjects: 1 + }; + + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 5).toUTCString()); + res.json(stats); + } catch (e) { + handleError(req, res, 500, e instanceof Error ? e.message : e); + } + } private getLiquidIcon(req: Request, res: Response) { const result = icons.getIconByAssetId(req.params.assetId); @@ -254,7 +332,6 @@ class LiquidRoutes { handleError(req, res, 500, e instanceof Error ? e.message : e); } } - } export default new LiquidRoutes(); diff --git a/docker-liquid/docker-compose-mainnet.yml b/docker-liquid/docker-compose-mainnet.yml new file mode 100644 index 0000000000..e490f73a1d --- /dev/null +++ b/docker-liquid/docker-compose-mainnet.yml @@ -0,0 +1,122 @@ +version: "3.7" + +services: + # MariaDB for mempool backend - run as root to fix permission issues + db: + container_name: liquid-mainnet-mempool-db + image: mariadb:10.5.21 + restart: on-failure + stop_grace_period: 1m + environment: + MYSQL_DATABASE: "mempool" + MYSQL_USER: "mempool" + MYSQL_PASSWORD: "mempool" + MYSQL_ROOT_PASSWORD: "admin" + volumes: + - ./mysql/mainnet-data:/var/lib/mysql + network_mode: host + + # Mempool backend API - connects to your local Elements daemon and Electrs + api: + container_name: liquid-mainnet-mempool-api + image: blockcore/mempool-backend:latest + user: "1000:1000" + restart: on-failure + stop_grace_period: 1m + depends_on: + - db + environment: + # Network configuration + MEMPOOL_NETWORK: "liquid" + MEMPOOL_BACKEND: "esplora" + MEMPOOL_ENABLED: "true" + + # CRITICAL: Angor configuration + ANGOR_ENABLED: "true" + + # Esplora configuration (connecting to your LOCAL electrs) + ESPLORA_REST_API_URL: "http://127.0.0.1:3000" + ESPLORA_REQUEST_TIMEOUT: "10000" + ESPLORA_FALLBACK_TIMEOUT: "10000" + + # Elements Core RPC configuration (connecting to your LOCAL elements daemon) + CORE_RPC_HOST: "127.0.0.1" + CORE_RPC_PORT: "7041" + CORE_RPC_USERNAME: "ritankar" + CORE_RPC_PASSWORD: "ritankar-liquid" + CORE_RPC_TIMEOUT: "60000" + + # Database configuration + DATABASE_ENABLED: "true" + DATABASE_HOST: "127.0.0.1" + DATABASE_PORT: "3306" + DATABASE_DATABASE: "mempool" + DATABASE_USERNAME: "mempool" + DATABASE_PASSWORD: "mempool" + + # Statistics and logging + STATISTICS_ENABLED: "true" + MEMPOOL_STDOUT_LOG_MIN_PRIORITY: "debug" + MEMPOOL_INDEXING_BLOCKS_AMOUNT: "8640" + MEMPOOL_BLOCKS_SUMMARIES_INDEXING: "true" + + # Mempool specific settings + MEMPOOL_CACHE_ENABLED: "true" + MEMPOOL_CACHE_DIR: "/backend/cache" + MEMPOOL_CLEAR_PROTECTION_MINUTES: "20" + MEMPOOL_RECOMMENDED_FEE_PERCENTILE: "50" + MEMPOOL_BLOCK_WEIGHT_UNITS: "4000000" + MEMPOOL_INITIAL_BLOCKS_AMOUNT: "8" + MEMPOOL_MEMPOOL_BLOCKS_AMOUNT: "8" + volumes: + - ./data-mainnet:/backend/cache + network_mode: host + command: "./wait-for-it.sh 127.0.0.1:3306 --timeout=720 --strict -- ./start.sh" + + # Mempool frontend - using your local image with Angor changes + web: + container_name: liquid-mainnet-mempool-web + image: mempool-frontend + user: "1000:1000" + restart: on-failure + stop_grace_period: 1m + depends_on: + - api + - db + environment: + # Frontend configuration + FRONTEND_HTTP_PORT: "8081" + BACKEND_MAINNET_HTTP_HOST: "127.0.0.1" + BACKEND_MAINNET_HTTP_PORT: "8999" + + # Network configuration + MAINNET_ENABLED: "false" + LIQUID_ENABLED: "true" + LIQUID_TESTNET_ENABLED: "false" + ROOT_NETWORK: "liquid" + BASE_MODULE: "liquid" + + # URLs + LIQUID_WEBSITE_URL: "http://localhost:8081" + MEMPOOL_WEBSITE_URL: "https://mempool.space" + + # CRITICAL: Angor configuration + ANGOR_ENABLED: "true" + + # Features + LIGHTNING: "false" + MINING_DASHBOARD: "false" + AUDIT: "false" + ACCELERATOR: "false" + ACCELERATOR_BUTTON: "false" + PUBLIC_ACCELERATIONS: "false" + HISTORICAL_PRICE: "true" + ADDITIONAL_CURRENCIES: "false" + + # Display settings + ITEMS_PER_PAGE: "10" + KEEP_BLOCKS_AMOUNT: "8" + MEMPOOL_BLOCKS_AMOUNT: "8" + BLOCK_WEIGHT_UNITS: "4000000" + network_mode: host + command: "./wait-for 127.0.0.1:3306 --timeout=720 -- nginx -g 'daemon off;'" \ No newline at end of file diff --git a/docker-liquid/docker-compose.yml b/docker-liquid/docker-compose.yml new file mode 100644 index 0000000000..118dc0847d --- /dev/null +++ b/docker-liquid/docker-compose.yml @@ -0,0 +1,122 @@ +version: "3.7" + +services: + # MariaDB for mempool backend - run as root to fix permission issues + db: + container_name: liquid-mempool-db + image: mariadb:10.5.21 + restart: on-failure + stop_grace_period: 1m + environment: + MYSQL_DATABASE: "mempool" + MYSQL_USER: "mempool" + MYSQL_PASSWORD: "mempool" + MYSQL_ROOT_PASSWORD: "admin" + volumes: + - ./mysql/data:/var/lib/mysql + network_mode: host + + # Mempool backend API - connects to your local Elements daemon and Electrs + api: + container_name: liquid-mempool-api + image: blockcore/mempool-backend:latest + user: "1000:1000" + restart: on-failure + stop_grace_period: 1m + depends_on: + - db + environment: + # Network configuration + MEMPOOL_NETWORK: "liquidtestnet" + MEMPOOL_BACKEND: "esplora" + MEMPOOL_ENABLED: "true" + + # CRITICAL: Angor configuration + ANGOR_ENABLED: "true" + + # Esplora configuration (connecting to your LOCAL electrs) + ESPLORA_REST_API_URL: "http://127.0.0.1:3001" + ESPLORA_REQUEST_TIMEOUT: "10000" + ESPLORA_FALLBACK_TIMEOUT: "10000" + + # Elements Core RPC configuration (connecting to your LOCAL elements daemon) + CORE_RPC_HOST: "127.0.0.1" + CORE_RPC_PORT: "18891" + CORE_RPC_USERNAME: "user" + CORE_RPC_PASSWORD: "password" + CORE_RPC_TIMEOUT: "60000" + + # Database configuration + DATABASE_ENABLED: "true" + DATABASE_HOST: "127.0.0.1" + DATABASE_PORT: "3306" + DATABASE_DATABASE: "mempool" + DATABASE_USERNAME: "mempool" + DATABASE_PASSWORD: "mempool" + + # Statistics and logging + STATISTICS_ENABLED: "true" + MEMPOOL_STDOUT_LOG_MIN_PRIORITY: "debug" + MEMPOOL_INDEXING_BLOCKS_AMOUNT: "8640" + MEMPOOL_BLOCKS_SUMMARIES_INDEXING: "true" + + # Mempool specific settings + MEMPOOL_CACHE_ENABLED: "true" + MEMPOOL_CACHE_DIR: "/backend/cache" + MEMPOOL_CLEAR_PROTECTION_MINUTES: "20" + MEMPOOL_RECOMMENDED_FEE_PERCENTILE: "50" + MEMPOOL_BLOCK_WEIGHT_UNITS: "4000000" + MEMPOOL_INITIAL_BLOCKS_AMOUNT: "8" + MEMPOOL_MEMPOOL_BLOCKS_AMOUNT: "8" + volumes: + - ./data:/backend/cache + network_mode: host + command: "./wait-for-it.sh 127.0.0.1:3306 --timeout=720 --strict -- ./start.sh" + + # Mempool frontend - using your local image with Angor changes + web: + container_name: liquid-mempool-web + image: mempool-frontend + user: "1000:1000" + restart: on-failure + stop_grace_period: 1m + depends_on: + - api + - db + environment: + # Frontend configuration + FRONTEND_HTTP_PORT: "8080" + BACKEND_MAINNET_HTTP_HOST: "127.0.0.1" + BACKEND_MAINNET_HTTP_PORT: "8999" + + # Network configuration + MAINNET_ENABLED: "false" + LIQUID_ENABLED: "true" + LIQUID_TESTNET_ENABLED: "true" + ROOT_NETWORK: "liquidtestnet" + BASE_MODULE: "liquid" + + # URLs + LIQUID_WEBSITE_URL: "http://localhost:8080" + MEMPOOL_WEBSITE_URL: "https://mempool.space" + + # CRITICAL: Angor configuration + ANGOR_ENABLED: "true" + + # Features + LIGHTNING: "false" + MINING_DASHBOARD: "false" + AUDIT: "false" + ACCELERATOR: "false" + ACCELERATOR_BUTTON: "false" + PUBLIC_ACCELERATIONS: "false" + HISTORICAL_PRICE: "true" + ADDITIONAL_CURRENCIES: "false" + + # Display settings + ITEMS_PER_PAGE: "10" + KEEP_BLOCKS_AMOUNT: "8" + MEMPOOL_BLOCKS_AMOUNT: "8" + BLOCK_WEIGHT_UNITS: "4000000" + network_mode: host + command: "./wait-for 127.0.0.1:3306 --timeout=720 -- nginx -g 'daemon off;'" \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore index c10a009469..176a6d444d 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -7,7 +7,6 @@ server.run.js # docker -Dockerfile entrypoint.sh nginx-mempool.conf nginx.conf diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000000..79962a327b --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,42 @@ +FROM node:20.15.0-bookworm-slim AS builder + +ARG commitHash +ENV DOCKER_COMMIT_HASH=${commitHash} +ENV CYPRESS_INSTALL_BINARY=0 + +WORKDIR /build +COPY . . +RUN apt-get update +RUN apt-get install -y build-essential rsync +RUN cp mempool-frontend-config.sample.json mempool-frontend-config.json +RUN npm install --omit=dev --omit=optional + +RUN npm run build + +FROM nginx:1.27.0-alpine + +WORKDIR /patch + +COPY --from=builder /build/entrypoint.sh . +COPY --from=builder /build/wait-for . +COPY --from=builder /build/dist/mempool /var/www/mempool +COPY --from=builder /build/nginx.conf /etc/nginx/ +COPY --from=builder /build/nginx-mempool.conf /etc/nginx/conf.d/ + +RUN chmod +x /patch/entrypoint.sh +RUN chmod +x /patch/wait-for + +RUN chown -R 1000:1000 /patch && chmod -R 755 /patch && \ + chown -R 1000:1000 /var/cache/nginx && \ + chown -R 1000:1000 /var/log/nginx && \ + chown -R 1000:1000 /etc/nginx/nginx.conf && \ + chown -R 1000:1000 /etc/nginx/conf.d && \ + chown -R 1000:1000 /var/www/mempool + +RUN touch /var/run/nginx.pid && \ + chown -R 1000:1000 /var/run/nginx.pid + +USER 1000 + +ENTRYPOINT ["/patch/entrypoint.sh"] +CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend/mempool-frontend-config.liquid-mainnet.json b/frontend/mempool-frontend-config.liquid-mainnet.json new file mode 100644 index 0000000000..00fa278d18 --- /dev/null +++ b/frontend/mempool-frontend-config.liquid-mainnet.json @@ -0,0 +1,28 @@ +{ + "TESTNET_ENABLED": true, + "TESTNET4_ENABLED": false, + "SIGNET_ENABLED": false, + "LIQUID_ENABLED": true, + "LIQUID_TESTNET_ENABLED": true, + "MAINNET_ENABLED": true, + "ITEMS_PER_PAGE": 10, + "KEEP_BLOCKS_AMOUNT": 8, + "NGINX_PROTOCOL": "http", + "NGINX_HOSTNAME": "192.168.0.144", + "NGINX_PORT": "8081", + "BLOCK_WEIGHT_UNITS": 4000000, + "MEMPOOL_BLOCKS_AMOUNT": 8, + "BASE_MODULE": "liquid", + "ROOT_NETWORK": "liquid", + "MEMPOOL_WEBSITE_URL": "https://mempool.space", + "LIQUID_WEBSITE_URL": "http://localhost:8081", + "MINING_DASHBOARD": false, + "AUDIT": false, + "LIGHTNING": false, + "HISTORICAL_PRICE": true, + "ADDITIONAL_CURRENCIES": false, + "ACCELERATOR": false, + "ACCELERATOR_BUTTON": false, + "PUBLIC_ACCELERATIONS": false, + "ANGOR_ENABLED": true +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index af84167511..e4fa970d28 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -59,7 +59,9 @@ "cypress:open:ci": "node update-config.js TESTNET_ENABLED=true TESTNET4_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:open", "cypress:run:ci": "node update-config.js TESTNET_ENABLED=true TESTNET4_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:run:record", "cypress:open:ci:staging": "node update-config.js TESTNET_ENABLED=true TESTNET4_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:open", - "cypress:run:ci:staging": "node update-config.js TESTNET_ENABLED=true TESTNET4_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:run:record" + "cypress:run:ci:staging": "node update-config.js TESTNET_ENABLED=true TESTNET4_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:run:record", + "serve:local": "ng serve --proxy-config proxy.conf.js --host 0.0.0.0 --port 4200", + "serve:local-liquid": "npm run config:defaults:liquid && ng serve --proxy-config proxy.conf.js --host 0.0.0.0 --port 4200" }, "dependencies": { "@angular-devkit/build-angular": "^17.3.1", diff --git a/frontend/proxy.conf.js b/frontend/proxy.conf.js index 05f7550e06..76743edda3 100644 --- a/frontend/proxy.conf.js +++ b/frontend/proxy.conf.js @@ -1,84 +1,64 @@ -const fs = require('fs'); - -let PROXY_CONFIG; -let configContent; - -const CONFIG_FILE_NAME = 'mempool-frontend-config.json'; - -try { - const rawConfig = fs.readFileSync(CONFIG_FILE_NAME); - configContent = JSON.parse(rawConfig); - console.log(`${CONFIG_FILE_NAME} file found, using provided config`); -} catch (e) { - console.log(e); - if (e.code !== 'ENOENT') { - throw new Error(e); - } else { - console.log(`${CONFIG_FILE_NAME} file not found, using default config`); - } -} - -PROXY_CONFIG = [ - { - context: ['*', - '/api/**', '!/api/v1/ws', - '!/liquid', '!/liquid/**', '!/liquid/', - '!/liquidtestnet', '!/liquidtestnet/**', '!/liquidtestnet/', - '/testnet/api/**', '/signet/api/**', '/testnet4/api/**' - ], - target: "https://mempool.space", - ws: true, - secure: false, - changeOrigin: true - }, - { - context: ['/api/v1/ws'], - target: "https://mempool.space", - ws: true, - secure: false, - changeOrigin: true, +// Simplified proxy configuration without external config file dependency +const PROXY_CONFIG = [ + { + context: [ + '/api/**', '!/api/v1/ws', + '!/liquid', '!/liquid/**', '!/liquid/', + '!/liquidtestnet', '!/liquidtestnet/**', '!/liquidtestnet/' + ], + target: "http://localhost:8999", + ws: true, + secure: false, + changeOrigin: true, + logLevel: 'debug' + }, + { + context: ['/api/v1/ws'], + target: "http://localhost:8999", + ws: true, + secure: false, + changeOrigin: true, + logLevel: 'debug' + }, + { + context: ['/api/liquid**', '/liquid/api/**'], + target: "http://localhost:8999", + pathRewrite: { + "^/api/liquid/": "/api/v1/" }, - { - context: ['/api/liquid**', '/liquid/api/**'], - target: "https://liquid.network", - pathRewrite: { - "^/api/liquid/": "/liquid/api" - }, - ws: true, - secure: false, - changeOrigin: true + ws: true, + secure: false, + changeOrigin: true, + logLevel: 'debug' + }, + { + context: ['/api/liquidtestnet**', '/liquidtestnet/api/**'], + target: "http://localhost:8999", + pathRewrite: { + "^/api/liquidtestnet/": "/api/v1/", + "^/liquidtestnet/api/": "/api/v1/" }, - { - context: ['/api/liquidtestnet**', '/liquidtestnet/api/**'], - target: "https://liquid.network", - ws: true, - secure: false, - changeOrigin: true - }, - { - context: ['/resources/mining-pools/**'], - target: "https://mempool.space", - secure: false, - changeOrigin: true + ws: true, + secure: false, + changeOrigin: true, + logLevel: 'debug' + }, + { + context: ['/resources/mining-pools/**'], + target: "https://mempool.space", + secure: false, + changeOrigin: true + }, + // Assets for liquid (since we're using liquid testnet) + { + context: [ + '/resources/assets.json', '/resources/assets.minimal.json', + '/resources/assets-testnet.json', '/resources/assets-testnet.minimal.json' + ], + target: "https://liquid.network", + secure: false, + changeOrigin: true, } ]; -if (configContent && configContent.BASE_MODULE == "liquid") { - PROXY_CONFIG.push({ - context: [ - '/resources/assets.json', '/resources/assets.minimal.json', - '/resources/assets-testnet.json', '/resources/assets-testnet.minimal.json'], - target: "https://liquid.network", - secure: false, - changeOrigin: true, - }); -} else { - PROXY_CONFIG.push({ - context: ['/resources/assets.json', '/resources/assets.minimal.json', '/resources/worldmap.json'], - target: "https://mempool.space", - secure: false, - changeOrigin: true, - }); -} - -module.exports = PROXY_CONFIG; +module.exports = PROXY_CONFIG; \ No newline at end of file diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index d1748312df..bd9bc1dfaf 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -217,6 +217,7 @@ let routes: Routes = [ }, ]; +// LIQUID ROUTING CONFIGURATION WITH ANGOR SUPPORT if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { routes = [ { @@ -263,6 +264,12 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { loadChildren: () => import('@app/liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), data: { preload: true }, }, + // This loads the main master page module which contains Angor routes + { + path: '', + loadChildren: () => import('@app/master-page.module').then(m => m.MasterPageModule), + data: { preload: true }, + }, { path: '', loadChildren: () => import ('@app/liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule), @@ -289,6 +296,31 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, ], }, + // Add missing routes that were in the main configuration + { + path: 'clock', + redirectTo: 'clock/mempool/0' + }, + { + path: 'clock/:mode', + redirectTo: 'clock/:mode/0' + }, + { + path: 'clock/:mode/:index', + component: ClockComponent, + }, + { + path: 'view/block/:id', + component: BlockViewComponent, + }, + { + path: 'view/mempool-block/:index', + component: MempoolBlockViewComponent, + }, + { + path: 'view/blocks', + component: EightBlocksComponent, + }, { path: 'status', data: { networks: ['bitcoin', 'liquid']}, @@ -317,4 +349,4 @@ if (!window['isMempoolSpaceBuild']) { preloadingStrategy: AppPreloadingStrategy })], }) -export class AppRoutingModule { } +export class AppRoutingModule { } \ No newline at end of file diff --git a/frontend/src/app/components/angor/angor-projects/angor-projects.component.html b/frontend/src/app/components/angor/angor-projects/angor-projects.component.html new file mode 100644 index 0000000000..44b86c42f5 --- /dev/null +++ b/frontend/src/app/components/angor/angor-projects/angor-projects.component.html @@ -0,0 +1,75 @@ +
+
+
+

Angor Projects

+

Crowdfunding projects on the Liquid Network

+
+
+ + +
+
+
+
+
{{ stats.totalProjects }}
+

Total Projects

+
+
+
+
+
+
+
{{ formatSats(stats.totalFunding) }}
+

Total Funding

+
+
+
+
+
+
+
{{ stats.activeProjects }}
+

Active Projects

+
+
+
+
+ + +
+
+
+
+
+
+
{{ project.name }}
+

{{ project.description }}

+ + {{ project.status | titlecase }} + +
+
+
+
{{ formatSats(project.currentFunding) }} / {{ formatSats(project.fundingGoal) }}
+
+
+
+
+ {{ getProgressPercentage(project) }}% funded +
+ +
+
+
+
+
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/angor/angor-projects/angor-projects.component.scss b/frontend/src/app/components/angor/angor-projects/angor-projects.component.scss new file mode 100644 index 0000000000..a23b6d5877 --- /dev/null +++ b/frontend/src/app/components/angor/angor-projects/angor-projects.component.scss @@ -0,0 +1,18 @@ +.card { + margin-bottom: 1rem; + border: 1px solid rgba(255, 255, 255, 0.1); + background-color: rgba(255, 255, 255, 0.05); +} + +.progress { + height: 10px; + background-color: rgba(255, 255, 255, 0.1); +} + +.badge { + font-size: 0.75rem; +} + +.text-muted { + opacity: 0.7; +} \ No newline at end of file diff --git a/frontend/src/app/components/angor/angor-projects/angor-projects.component.ts b/frontend/src/app/components/angor/angor-projects/angor-projects.component.ts new file mode 100644 index 0000000000..170999a482 --- /dev/null +++ b/frontend/src/app/components/angor/angor-projects/angor-projects.component.ts @@ -0,0 +1,28 @@ +import { Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { AngorService, AngorProject, AngorStats } from '../../../services/angor.service'; + +@Component({ + selector: 'app-angor-projects', + templateUrl: './angor-projects.component.html', + styleUrls: ['./angor-projects.component.scss'] +}) +export class AngorProjectsComponent implements OnInit { + projects$: Observable; + stats$: Observable; + + constructor(private angorService: AngorService) { } + + ngOnInit(): void { + this.projects$ = this.angorService.getProjects(); + this.stats$ = this.angorService.getStats(); + } + + getProgressPercentage(project: AngorProject): number { + return Math.round((project.currentFunding / project.fundingGoal) * 100); + } + + formatSats(amount: number): string { + return (amount / 100000000).toFixed(2) + ' L-BTC'; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html index 7e39d9341e..fcc3866d66 100644 --- a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html +++ b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html @@ -78,6 +78,9 @@