From fee9d2cce5fded289906ef61163d117229730bbd Mon Sep 17 00:00:00 2001 From: dvirlabs <114520947+dvirlabs@users.noreply.github.com> Date: Tue, 3 Jun 2025 14:38:47 +0300 Subject: [PATCH 1/3] Add minio connection --- backend/apps.yaml | 4 +--- backend/main.py | 24 ++++++++++++++++++++++++ frontend/src/components/AppCard.jsx | 15 +++++++++++++-- frontend/src/services/api.js | 7 +++++++ 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/backend/apps.yaml b/backend/apps.yaml index 120f639..e29b753 100644 --- a/backend/apps.yaml +++ b/backend/apps.yaml @@ -22,6 +22,4 @@ sections: icon: http://192.168.10.118:1111/icons/woodpecker-ci.svg name: Woodpecker url: https://woodpecker.dvirlabs.com - name: Dev-tools - - + name: Dev-tools \ No newline at end of file diff --git a/backend/main.py b/backend/main.py index 51ad3d0..2a70f2b 100644 --- a/backend/main.py +++ b/backend/main.py @@ -3,6 +3,8 @@ from fastapi.middleware.cors import CORSMiddleware import yaml from pathlib import Path from pydantic import BaseModel +from minio import Minio +from datetime import timedelta app = FastAPI() @@ -15,6 +17,16 @@ app.add_middleware( allow_headers=["*"], ) +# MinIO connection with access and secret keys +minio_client = Minio( + "minio.dvirlabs.com", + access_key="YOUR_MINIO_ACCESS_KEY", + secret_key="YOUR_MINIO_SECRET_KEY", + secure=True # Set to False if using HTTP +) + +BUCKET = "navix-icons" + @app.get("/") async def root(): return {"message": "Welcome to the FastAPI application!"} @@ -63,6 +75,18 @@ def add_app(entry: AppEntry): return {"status": "added"} +@app.get("/icon/{filename}") +def get_presigned_icon_url(filename: str): + try: + url = minio_client.presigned_get_object( + bucket_name=BUCKET, + object_name=filename, + expires=timedelta(hours=1) + ) + return {"url": url} + except Exception as e: + return {"error": str(e)} + if __name__ == "__main__": import uvicorn diff --git a/frontend/src/components/AppCard.jsx b/frontend/src/components/AppCard.jsx index 95cebe9..4e2fdc6 100644 --- a/frontend/src/components/AppCard.jsx +++ b/frontend/src/components/AppCard.jsx @@ -1,10 +1,21 @@ -import '../style/AppCard.css'; +import { useEffect, useState } from 'react'; +import { getIconUrl } from '../services/api'; function AppCard({ app }) { + const [iconUrl, setIconUrl] = useState(''); + + useEffect(() => { + if (app.icon) { + getIconUrl(app.icon) + .then(setIconUrl) + .catch(() => setIconUrl('fallback-icon.svg')); + } + }, [app.icon]); + return (
- {app.name} + {app.name}

{app.name}

{app.description}

diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index 290463b..31231bb 100644 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -12,4 +12,11 @@ export async function addAppToSection({ section, app }) { }); if (!res.ok) throw new Error(await res.text()); return res.json(); +} + +export async function getIconUrl(filename) { + const res = await fetch(`/icon/${filename}`); + if (!res.ok) throw new Error(`Failed to fetch icon for ${filename}`); + const data = await res.json(); + return data.url; } \ No newline at end of file From 36478eb5075612d2dbddb076920747a14b707594 Mon Sep 17 00:00:00 2001 From: dvirlabs <114520947+dvirlabs@users.noreply.github.com> Date: Wed, 4 Jun 2025 00:36:06 +0300 Subject: [PATCH 2/3] Fix pull from minio --- backend/.env | 4 +++ backend/__pycache__/main.cpython-312.pyc | Bin 0 -> 4296 bytes backend/apps.yaml | 10 +++--- backend/main.py | 33 ++++++++++-------- frontend/src/components/AppCard.jsx | 42 +++++++++++++++-------- frontend/src/services/api.js | 4 +-- frontend/vite.config.js | 1 + 7 files changed, 58 insertions(+), 36 deletions(-) create mode 100644 backend/.env create mode 100644 backend/__pycache__/main.cpython-312.pyc diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..901386b --- /dev/null +++ b/backend/.env @@ -0,0 +1,4 @@ +MINIO_ACCESS_KEY=TDJvsBmbkpUXpCw5M7LA +MINIO_SECRET_KEY=n9scR7W0MZy6FF0bznV98fSgXpdebIQjqZvEr1Yu +MINIO_ENDPOINT=s3.dvirlabs.com +MINIO_BUCKET=navix-icons \ No newline at end of file diff --git a/backend/__pycache__/main.cpython-312.pyc b/backend/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d8011592cf8c3cde3b3553ce33edd5ca69b4a52 GIT binary patch literal 4296 zcmb6cTWl29_1<~x&hE~>ytda*@PpTqVkeZOQt3NN-FW0&sNPxrT(n5sJIi5A~hfF-vX9s`RX~dJG+i$tL?1k z+;h)8=iYPAoR|L+4EhN?AC4ay`;Cu~zvIMxycOixpE*M2iAq#zkPJ;yia2L*89vE7 zIBy6UF)2E@V0e-qSQia%MoLN*nlI@Cn#b^G0?B|w^BQs{m<&3&WP~!|WZ1!dMkEtW zMk(QlM(c~s3yfgER@2E+-m>RrC%XqQ@ zPF$ZSROM;hkSAQ_iEqdgsq!=dPgrZv8nw6@_%wP?sOZqF)+JluuHSL>tBq>Rl~(Jo z6ZdAxHrOekwYzKM2Wk>7rZ!9k2uW_+66lwbx*5}YFl;ZcCG6Ir*66~RQ2Anfi|0~G9B7hkm+0{vrXHsc3p(8$Xdah z1Y<{X2i$(g_?g<7E~a*?Thy&!+ik9GDVY=|g~axMB1?kW-Xn@>^_@NjXsG|xncv0sT|Ws^{i<}%hNOirmUqahUs!-G;1uUR;Oc{qH4^v zLnn_79Xpj892z)%>e$d(J5rwa_4f}B52sEHo^vU~gZ*a)&pH&j%yand{u6^|6TIzL z(rL{!Qx~;K+iz-cxaHs;z$chC704==^eemcG+g-w?M=Iu#1Rqzxd!jEIshjK#dJVb zPN&1<_xxp=;D>B)1~yklwW;p!YeqVk(K@VLhc%{kRLtC=UidhSyI;FP-v%7G}?+k};;lUc|p~ zO%Jf4r$4(3V4he2%g;Dc*F3H#n}TZ!tCD-jDoK^UhsJ~FRdSVBZcZiRQkl9MP7~Mn z(;WCW{9IW|hDkShg_!g*N69((0N~B!3V)7(@r5B<)ELXLsaW-P9od}KaUnO6Rc*== zqV4NDeR?=`4yRH|7A)*Ivzbac&dgfjM8{?fUC{xU#?3f@pOIyM z@Y=C5h}IW{`emPd{d;8@WMBW{U9hsj#YHfIbMZ+Y5{v|w9L!p5()L0)mv03hZ7AVk zJ+KGsMSvfVy@Fsj0!K>>!&RE^!6|~*;WcFdpm{KI?agIh~6daDt*1mqeI*4A7MBttG<8^@zH+{N7d z;cAL8o!u{x>55Jp?7og(|NoiLGl?HoZA8hs_V#RGZ_Kf4o@qK={W?w;_l?}8dQ}Na z#o7#Z>ks7&wwCVlSMr;0$d)QjZL$tHne7B%3*dZaf^#NpA5!RL+hamdPMEd`-l?ex zKkGz(3?tT!U<-n+2wnwXdteW!CJYmdEvS0hs-!}7B9l)D4CB}KLV0B9Y!A<_r039anf3>j-*V1>G_o8GU3(f1Pz!^L1H0wx>iA1GC=cK;7JHHxDgnzs~$JQw+Sc zEXU@K+&uMA?pl(&7GM2%??-z-KKRkWV)wTn$b0@KM@wE1EBT2pGUs2CS|3VXOH$Wz zee?XuJ13VLTISE)IX8d)&iP`))#q+kci!Yos_`9|Nk^fUyd?3nyE{X#o@8?l* zpxrwa>BX$v`{N6`p<$xEYD+GLj9J-zTX;)=kLFA{|EPv7yO52`X2@2 ziw$@AMfpKsS3%h2WLhjp*yp5DQxcB*z+0!hFZVb<{f*6_3vUZ!In%O*e2!VRhiOg? z-)Va3Ryo8%7%4R+Y!8?W8tSrzF-I~? zE{?5j+1V?VHgxz3wx`_3+B`@&?H(EjeFQ4BCl5_K#HSPM*rk+98`dc_3{-3k%yQ+! z@8Oh0+dFXyz7m7-&nB{KD)^LcgE-P$BQ)jgcE2d3e=SlTag7CoYhwd3@b86qqCDM1O~`hoOq+@ zy{6fnMcPo}8tCS^?>;3kplqWL8f+xP?Cut&rW^8 jH-8ED5yCS^Z=87V#GHSz`F{Op>R*Nal1PLWCsO_c?!l~O literal 0 HcmV?d00001 diff --git a/backend/apps.yaml b/backend/apps.yaml index e29b753..5d815ea 100644 --- a/backend/apps.yaml +++ b/backend/apps.yaml @@ -1,25 +1,25 @@ sections: - apps: - description: Dashboards - icon: http://192.168.10.118:1111/icons/grafana.svg + icon: grafana.svg name: Grafana url: https://grafana.dvirlabs.com - description: Monitoring - icon: http://192.168.10.118:1111/icons/prometheus.svg + icon: prometheus.svg name: Prometheus url: https://prometheus.dvirlabs.com name: Monitoring - apps: - description: Git server - icon: http://192.168.10.118:1111/icons/gitea.svg + icon: gitea.svg name: Gitea url: https://git.dvirlabs.com - description: Container registry - icon: http://192.168.10.118:1111/icons/harbor.svg + icon: harbor.svg name: Harbor url: https://harbor.dvirlabs.com - description: CI/CD - icon: http://192.168.10.118:1111/icons/woodpecker-ci.svg + icon: woodpecker-ci.svg name: Woodpecker url: https://woodpecker.dvirlabs.com name: Dev-tools \ No newline at end of file diff --git a/backend/main.py b/backend/main.py index 2a70f2b..7f6354f 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,5 +1,8 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import JSONResponse +import os +from dotenv import load_dotenv import yaml from pathlib import Path from pydantic import BaseModel @@ -17,12 +20,20 @@ app.add_middleware( allow_headers=["*"], ) +load_dotenv() + +# Load ENV variables +MINIO_ENDPOINT = os.getenv("MINIO_ENDPOINT") +MINIO_ACCESS_KEY = os.getenv("MINIO_ACCESS_KEY") +MINIO_SECRET_KEY = os.getenv("MINIO_SECRET_KEY") +MINIO_BUCKET = os.getenv("MINIO_BUCKET") + # MinIO connection with access and secret keys minio_client = Minio( - "minio.dvirlabs.com", - access_key="YOUR_MINIO_ACCESS_KEY", - secret_key="YOUR_MINIO_SECRET_KEY", - secure=True # Set to False if using HTTP + MINIO_ENDPOINT, + access_key=MINIO_ACCESS_KEY, + secret_key=MINIO_SECRET_KEY, + secure=True ) BUCKET = "navix-icons" @@ -76,16 +87,10 @@ def add_app(entry: AppEntry): return {"status": "added"} @app.get("/icon/{filename}") -def get_presigned_icon_url(filename: str): - try: - url = minio_client.presigned_get_object( - bucket_name=BUCKET, - object_name=filename, - expires=timedelta(hours=1) - ) - return {"url": url} - except Exception as e: - return {"error": str(e)} +def get_public_icon_url(filename: str): + url = f"https://{MINIO_ENDPOINT}/{MINIO_BUCKET}/{filename}" + return JSONResponse(content={"url": url}) + if __name__ == "__main__": diff --git a/frontend/src/components/AppCard.jsx b/frontend/src/components/AppCard.jsx index 4e2fdc6..9fe7f08 100644 --- a/frontend/src/components/AppCard.jsx +++ b/frontend/src/components/AppCard.jsx @@ -1,25 +1,37 @@ import { useEffect, useState } from 'react'; -import { getIconUrl } from '../services/api'; +import '../style/AppCard.css'; +import { getIconUrl } from '../services/api'; function AppCard({ app }) { - const [iconUrl, setIconUrl] = useState(''); + const [iconUrl, setIconUrl] = useState(null); useEffect(() => { - if (app.icon) { - getIconUrl(app.icon) - .then(setIconUrl) - .catch(() => setIconUrl('fallback-icon.svg')); - } - }, [app.icon]); + if (app.icon) { + getIconUrl(app.icon) + .then((url) => { + console.log('Presigned icon URL for', app.name, ':', url); + setIconUrl(url); + }) + .catch((err) => { + console.error(`Failed to load icon for ${app.name}:`, err); + }); + } +}, [app.icon, app.name]); return ( -
-
- {app.name} -
-

{app.name}

-

{app.description}

-
+
+ +
+ {iconUrl ? ( + {app.name} + ) : ( + ⚠️ + )} +
+

{app.name}

+

{app.description}

+
+
); } diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index 31231bb..d9929ed 100644 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -18,5 +18,5 @@ export async function getIconUrl(filename) { const res = await fetch(`/icon/${filename}`); if (!res.ok) throw new Error(`Failed to fetch icon for ${filename}`); const data = await res.json(); - return data.url; -} \ No newline at end of file + return data.url; // ✅ must return the actual URL string +} diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 04886e1..ea8be0a 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -8,6 +8,7 @@ export default defineConfig({ proxy: { '/apps': 'http://localhost:8000', '/add_app': 'http://localhost:8000', + '/icon': 'http://localhost:8000', } } }); From 4c29294d6ab9003b9c188c81060e8ba06d9af4cc Mon Sep 17 00:00:00 2001 From: dvirlabs <114520947+dvirlabs@users.noreply.github.com> Date: Wed, 4 Jun 2025 00:54:57 +0300 Subject: [PATCH 3/3] Change the pipeline --- .woodpecker.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.woodpecker.yaml b/.woodpecker.yaml index 47f62a2..7a354da 100644 --- a/.woodpecker.yaml +++ b/.woodpecker.yaml @@ -14,7 +14,7 @@ steps: - echo "TAGS=latest,$TAG_DATE-$SHORT_SHA" > .tags.env - name: build-frontend - image: woodpeckerci/plugin-docker + image: woodpeckerci/plugin-docker-buildx settings: repo: harbor.dvirlabs.com/my-apps/navix-frontend tag_file: .tags.env @@ -27,7 +27,7 @@ steps: from_secret: harbor_password - name: build-backend - image: woodpeckerci/plugin-docker + image: woodpeckerci/plugin-docker-buildx settings: repo: harbor.dvirlabs.com/my-apps/navix-backend tag_file: .tags.env