From 9cfc07a510b26d48f12dfb0f622bc604bf3a7a82 Mon Sep 17 00:00:00 2001 From: dvirlabs Date: Fri, 8 Aug 2025 12:31:59 +0300 Subject: [PATCH] Add Dockerfile for back and front, Set the runtime env for front --- backend/Dockerfile | 46 ++++++++++-------- backend/__pycache__/config.cpython-310.pyc | Bin 590 -> 0 bytes backend/__pycache__/config.cpython-312.pyc | Bin 1044 -> 0 bytes .../__pycache__/downloader.cpython-310.pyc | Bin 1804 -> 0 bytes .../__pycache__/downloader.cpython-312.pyc | Bin 2577 -> 0 bytes backend/__pycache__/main.cpython-310.pyc | Bin 1172 -> 0 bytes backend/__pycache__/main.cpython-312.pyc | Bin 1674 -> 0 bytes frontend/.dockerignore | 6 +++ frontend/10-generate-env.sh | 10 ++++ frontend/Dockerfile | 16 ++++++ frontend/index.html | 6 +-- frontend/nginx.conf | 16 ++++++ frontend/public/env.js.template | 4 ++ frontend/src/services/api.js | 21 ++++---- 14 files changed, 94 insertions(+), 31 deletions(-) delete mode 100644 backend/__pycache__/config.cpython-310.pyc delete mode 100644 backend/__pycache__/config.cpython-312.pyc delete mode 100644 backend/__pycache__/downloader.cpython-310.pyc delete mode 100644 backend/__pycache__/downloader.cpython-312.pyc delete mode 100644 backend/__pycache__/main.cpython-310.pyc delete mode 100644 backend/__pycache__/main.cpython-312.pyc create mode 100644 frontend/.dockerignore create mode 100644 frontend/10-generate-env.sh create mode 100644 frontend/Dockerfile create mode 100644 frontend/nginx.conf create mode 100644 frontend/public/env.js.template diff --git a/backend/Dockerfile b/backend/Dockerfile index 68e41d5..817d09f 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,29 +1,37 @@ -FROM python:3.11-slim +# ---- Base ---- +FROM python:3.11-slim-bookworm -# Install ffmpeg + curl and download latest yt-dlp binary -RUN apt update && \ - apt install -y curl ffmpeg && \ - curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp && \ - chmod a+rx /usr/local/bin/yt-dlp && \ - apt clean && rm -rf /var/lib/apt/lists/* +# עדכון מערכת והתקנת FFmpeg (כולל ffprobe) +RUN apt-get update \ + && apt-get install -y --no-install-recommends ffmpeg \ + && rm -rf /var/lib/apt/lists/* -# Set working directory +# הגדרות סביבת עבודה בסיסיות +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PIP_NO_CACHE_DIR=1 + +# ספריית האפליקציה WORKDIR /app -# Copy project files -COPY . . - -# Install Python dependencies +# התקנת תלויות +# (משתמש ב־requirements.txt שהבאת) +COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -# Create volume path (optional - will be mounted later in K8s) -RUN mkdir -p /music +# קבצי האפליקציה +COPY . . -# Declare env var for clean override later in Helm -ENV MUSIC_DIR=/music +# תיקיית המוזיקה (הקוד גם יוצר אותה, אבל נגדיר כ־VOLUME לנוחות) +VOLUME ["/app/music"] -# Expose FastAPI port +# פורט האפליקציה EXPOSE 8000 -# Start app -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] +# ריצה תחת משתמש לא־רות +RUN useradd -ms /bin/bash appuser && chown -R appuser:appuser /app +USER appuser + +# הפעלה +# אם תרצה workers: הוסף --workers 2 (או לפי הצורך) +ENTRYPOINT ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers"] diff --git a/backend/__pycache__/config.cpython-310.pyc b/backend/__pycache__/config.cpython-310.pyc deleted file mode 100644 index cd0b255ef5b77da6110b779ee0c5308937aaf146..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 590 zcmYjP&2AGh5FXF|WSeXPLV!4OLdu1eI3v{1Rwxn;QdZ^Qi`m9*x@EIY?F|y=aON4B zBhSE__{xb_=!vltiH_vi`DVu7&X4tQ*awpP-#@Pl1o-2@Lx~6+)3I+zNsz1o?!VZu z6>?;ta0?7RfQ)4P17y5K9;s*xJeI74aq^e$!EDUjpwX2SQg^ETw(!n}k`g$kW51A6 zz?lS&D!L;Qi96(pif+=oo=SGiz6y30^pWcC*pBhR0r{+44JZ%EJ8V%Ud>DABJmMM2 zgo;XCR;sS6+og3!=NIMlM4V3NE?I4Cy>L%vlh4!B`KR+UQJzd@;$r^MQ3sD)CPZ7U zln|~j#7fIe+i-d z&R?@5QW@l!v#0oi#rPOQY@C<@MO?byh@s^}2)N81X7m4ld+WK+&))7kf5!+}|9FU; Jr8vTV{tr+Vl5PM1 diff --git a/backend/__pycache__/config.cpython-312.pyc b/backend/__pycache__/config.cpython-312.pyc deleted file mode 100644 index b05e2ab6083db31e1012dc341105e7e9f087e9c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1044 zcmb7D&rcIU6rTN6wk<26RAh*O{mLJ860h-X@VYA6(=?rB{yNkP95)LMs zaDaHgo{I4xJypYnBmaXJO+An?CN-YCX*ig0a;EJ92RQf+Z@xG0oA1red;3XIB7oXE zVQ+1c2jC|Q0u#I7^a%wufB@o3KxZpo;>x_vbASh!KkU+2lmSTC21q=lTFi8TGK7Gk zi(6bm`o$89364Zdj)@?0d~9##*=As&E;wD~SYZrw4gsBKQbjo+U95;h{CPo%a(XXv z0V?(d=wijvA=PK*6|Xtf-^pM0*Dwbzb2=DD0{vjsqyKUG>hDmms1v1M(tYsrN{Y>6 zNms6YGvQK`Zfr@35Rr4(_bt2Nk?_KDZf*vq=az_6u6S0S#1^NX&83&J3$rjcGqnhp zm*xrWVIPqpw9PVxkVGIXJE&4(xEI3b6|>aM@Qz191ZV!XOsDHW7)&5XP`~XEg4xG zr(J6k8)@vV`_6_@uzYM9e#OSfrQwR1U&l5w@{YY~74B|qxe}9Pj%j-@sM`ju{^2*V znp#!&AAaa*_TPK?^dze6X}j89ayPj@{53k(8n{{0s#+~oOCXmzcTs;7<~G)E_z z1IZovBtBTnR5P_~HG44K$ka29`TG3P(D8V4>~S-m*$ubiBRk=idS^fOUVW>69@UOx zO?BdnIOq-q_en^uLNN(-{u5US5=8d8{c>-yz4i$<9cS`HrxB0b+cab&?u}CSTi`hE f8wh^~BR@cNTlp(Sxa1$e+z{xEzaD=jw?+N|uie^U diff --git a/backend/__pycache__/downloader.cpython-310.pyc b/backend/__pycache__/downloader.cpython-310.pyc deleted file mode 100644 index 665b4d12df9f6f8386091ab926391611f6528cb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1804 zcmZux&2Jk;6rY*>^d@l=x8+Mg@_|5C(3q;XC{&=7NR?=W2nZzytj*5Y-g>>W%#54Z zvQLTBT!24-R!Wcg7dUg~511?BkUJ*?E$?mOG<@u8X5YMh^S*yiE-XX@p5K1?zW0+) z$gg;~`nYiT7?%D9HcmLr$u53bP6uq4QKX&R8MwPH(5~>fC;X1feI6XSyTKJs$ZNm} zdBo?A$Zie)bQquK3&4#y-6nDU3~Z3Cm|FMq#Pr}BYGF)PbhY)q9SSwFz7l4rij<<{ z#e+11wE;^n!p7-@JSJtp8Gb>rvt`3FPeysB&Dg{Jqf>H96$Yn*+4jdfEhVLCnPDNg zl4WZzN&BMUEiU(qTqazo)pBI#GTojj^Ux&PUZe{1_#eLVr$0iOk16-wB0%}i zylnz9r0=TR|Bwle$yFH+b-)#ePzyd(iOFO!j<)+*Swex=n$|O!$;Ei7ndzqPNo9m; znqE>g&(^`Uao8MW#n1>nUPOsp7TuWuB+=;#6Fb(;ijK5VR-|%J<^ojI;qEE3bA#ld ztwkyeuHldsv%`6|t3(IwPL{)!dCUa%QAAxD(&cKof4a{2-I?QUm4NvR`PKDYrb({v ztzIxHow1`{10CunZm;83HQEiNDswmgSJ@o=*VvbJ>IUi!ATFx~Gy#Yb?0~ALeg?u2 zLm#n2`pAJILz%w=NE&xS4yn36AydkoDJvF@S8 z^(jN3X0*$Uk03pSZs2I6{xq06Cg9g5&LP?gPihnBiXWDy?!-NbCbSD3KJ~taYP;|r zIxrR%`BDa5e~0LueM$+wJFsT_xUuzR%xq95N)$%hP#ge6+p@noo*Nl05|#Gue7H8Q z?+L)lkY}=m)VR@Fxm7hytXEfVi31aB+t}P3l%o6Tfl*0nK0#sY49dHp-|35yt(Qu4 zgi?a1l8aQm4LP*+^GgqfNqAzCvjbpe%K)r6uzp9XL1JtG5H!$Kph0R=DwztcrP6jD z{-(W)(%Yzzv*Ca9)ZS+LiZw`h!OPd{; z!?b~qYQ7R_LVF4w&nZN_LfqQg!>_gDVx_eUQ=h5OQtU|67Tye8PT?M#seIWTT7x!@NX&ek8nWyu# L!5U8JG@`!&erLl^ diff --git a/backend/__pycache__/downloader.cpython-312.pyc b/backend/__pycache__/downloader.cpython-312.pyc deleted file mode 100644 index 39b8df2cb65de14963c7696bab8e7f895c802ac7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2577 zcma(TOKcm*b!I>O>Q7N0Q*{=mY>N%5GF4+WUYMOvTwW=Tr6k*1vm z-@JMA=FNNae*C1jH-ccit^Owe_W(j)$VGQ(9pvD3K(-M<1lwp)&WeqT%A$fLuG(r* zThsv8s7`bm$Y~^SLt6|!vJS#JL_#;vVpvjh3JC)xQpKsreR;`T3VYhmOp%8<#HkQg zX_b5WFDjH(y#S-4!b(}(QAbV+{8acIi~s=Da2;Jk>zGS56p7EHlse~y%C=dxEiP)h zgfG5_-os3XBU}nI9;UPBmKHc=e2HAMn7#DV5=}Gf8eK|LUJy=sY1I&XuJ`3qsKpup4mBld%-<%#?{Uo zz!rMU9%+kj&mHg~n$*4fF=yMq79b zfe-PG(1uc1d}R);ks3<-rTl{r0KBFAZ+oN|_EKHBsTJTlmXJRD+80o}FYq>m+f9w= zHQ)z4@UA|O;JjK$|KI0>H`T|_S5XzELJ&2i!c1W~rCMaZV3o@djOPqb7nZPTZP2i| z!SfCilo=v#mW=!7!OB|5C|ae8pu9F9Ic%r2+GPM2sjF0^bWgKNIme4wrL0pd+Z03$ z!&xs{G}kknW}On@u&_$q1(z>r7{=yi+nd2m&a&ZD?NnFA3^U=P4te^#?PN`x&rEj+ zG8lOsz`wxNvC-`p-h83)modM*Y_zS5f2~pMTfie@nGXQ?_a^$M63|WD>O(!T&AE2G zKAJ2ty8Tr8El{_SINTG5QN5-C&l95?SZl#2Heg9G!GBQR@0`a-NMrm_N8NKwucHl2 z)D5LHDx|vqeIF`g9fI~<1^DrXLiBIe%nxsc1WAa5-%&_}L@m6k2$`tFerMbRDOL=3YxFUa)L3S`yp~(RHZM8K*E?i&h1v zCd=lZ{qDJ1-wK8DRw0)2jKpd~&zzj{Gcm=dPfpQwk>cLa>};`2S6^Ni%*=|HB(tX$ z%V$BpTA)?0ugqwUGDb<(AvDX9V2Ib(KJ+RTCNYJ1e;tU{1o$`=^a44D6;0s

hhy zRFL3sQD#n-a_%th^}ud6-^nsBQgXWWUp6g^!pUottvH<4`m5qQ#4a07SymK&&Wjv2 z4^lgQAy2afqgdfq*2pVD(#bH&rk%qj^lP!$=t*$ZE7*Z=fSpt5xuS1RDQK`#V6ewSrWOVOb!u`{44_f)`S z=j9b!aQI^&s}PKKqV{&hWH^*L_I=ovQNkYr*hF`=(67h#!-?H+Vo#gc*G}weCvJc5 zkCU!;;*R#izXjvB=$+t1Gd8vrYQ~SsMgCkG@zZ~x!2y9+@a2)9oHpu+6sK70{LFgn0xGk8%?`f`YZhj!b2{r?MF|=el(+gG^1aL1i@=( zfFxsF*e9<8lo6fKq zSjsfz7Xh?XjPYlv_j5G%IZCuNjL+WUULk-9CLt&n`NOx`4!UE338CYx@w zMTq4Z{sDvp2PF8H=7hwlcTNZaZ@in(8O`&2pU0l>d+*6stKreu`}ORPBR2PGWi==JleHG%Bu^Zxqd> z2_BD6TfBMN#t7pjBYX?%4*%f&$gY4eU;{U>xnxO)cTP8Xgsm~GS9=-Ina)qQ@z&44 z8Fp|JZ!g&haEot^7;fXvci|Nz+k6Li&zLGnN09F(yL|U_5BG2%-@-d|_uEUSI~Y`} zd-pZD^t!%{o@Cm5^!(6%Df$g&Hz`Ptyv(J|LkS!zzWeempf z@Khk?e4Z(OL{tmqyv${Wsg~uKh*)!DL^;+c4)B*kn^c}HHeYBW?>~JpI6O!{K0JPO z-^M4ilB1H-MfiU2Vees=+0Y1+a~qv=rAdN?2PCtV<++@vQi-uB^$Hf;OeC)1gl9+} zE$aQ1px$fdY=UhRqD*sMj?Kh|Upc2ka2dE6)DWI)j{{oz357-5c$eN4yaEFV$OK%% zFM$3MzVHTK;2C!D9(b>)oQv4_7?|LaiizQpeG5qZ*K7`RuUk8^HEl98(>Btx;gD-h zVxple4KI!OlQfXyjq4-nP$IWA#HrduPLr)xv{{1 z^VB=U{B^Idq%?hVliB{4+3<{)*e^0s_NEs$US(&8PU|)W#$wopHgq8VhW)GFb^ndi z)P0bqi`YFMWz*>*q6)bw9@7yUh;k&mfej{78yifeGPa(}G3EK3=T4aGiSE9QM$}{3 zR47*r<#K&(WAldZk%g+g^tIi+6<@5|tT&WOtC|zWq_&Nf1+JuY1jgQzc4ceez-38DjeX&#gb^M%{6`R@Td`;Av{3*}0&bOY5syS!MT)aId=S sgF@mg=O4Ou(hn$XdrfFU3{BRA4(re}U<`t4*8Yy8yAVe)>_hO+f5}in`Tzg` diff --git a/backend/__pycache__/main.cpython-312.pyc b/backend/__pycache__/main.cpython-312.pyc deleted file mode 100644 index 6dbeabda26c39c26cba435511cf6558a0f1a22d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1674 zcmZuxO>7fK6n?Wm_WCa&e-3ddL;@s?sQJ0Hf(R8Q0O9_XHUi#L~CL9=R-@M82sE|CVXnC!o=XDb3G}ow@d9%UmZln^; zM;pB1T6qiYrW>oo^YMl*;wCD|e3F0$%$mrA7j5kora6cpKyX{Kz~^{0zIcgl_@ zLb1Z+xKB65ab{BtnvER{6Uw~|nvF@sD~?xmnOBzM%KRdzw6xLO1}wpV;?TnSJd#@w z1G#Cv0%3!$5*he379~`3$QAMpS=K}QFzg1o(Qbe}zw9y&bhI!k5XS@ zb?m=29o26{JDznt^B&xdogn(R>3;o36B%U{i$Ma#;;a?yN9}5LHi`ok{G5+=VX21W zjrmza8RNc?%BcEWs+jASaV(~o8z2#kaCW!JUTMBi@emTm8+}m`?BSSG=D|cLOn8z}hYJYEEa>tWO$2wGt zO=)rKaHW@-#k6W9{(9>aX~;=Cb4bLllGI z7QY}tObPh|4zIxB=dk}d9C-?{zhTdF=zRf+IqhcRT4Mh6qvXz&`qbseMk{* $TARGET" +sed "s|\$VITE_API_URL|${VITE_API_URL}|g" "$TEMPLATE" > "$TARGET" +echo "[entrypoint] Done." \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..e42f639 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,16 @@ +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm install --legacy-peer-deps +COPY . . +RUN npm run build + +FROM nginx:alpine +RUN apk add --no-cache dos2unix gettext +COPY --from=builder /app/dist /usr/share/nginx/html +COPY public/env.js.template /etc/env/env.js.template +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY 10-generate-env.sh /docker-entrypoint.d/10-generate-env.sh +RUN dos2unix /docker-entrypoint.d/10-generate-env.sh && chmod +x /docker-entrypoint.d/10-generate-env.sh +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html index 0c589ec..767b3c7 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,12 +2,12 @@ - - Vite + React + Tunedrop +

- + \ No newline at end of file diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..2763296 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,16 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri /index.html; + } + + location = /env.js { + alias /usr/share/nginx/html/env.js; + add_header Cache-Control "no-store"; + } +} \ No newline at end of file diff --git a/frontend/public/env.js.template b/frontend/public/env.js.template new file mode 100644 index 0000000..09daa80 --- /dev/null +++ b/frontend/public/env.js.template @@ -0,0 +1,4 @@ +// Replaced at container start +window.__ENV = { + VITE_API_URL: "$VITE_API_URL" +}; diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index 4f7913b..33a5dbd 100644 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -1,16 +1,19 @@ -const BASE_URL = import.meta.env.VITE_API_URL || "http://localhost:8000"; +const runtimeBase = typeof window !== "undefined" && window.__ENV && window.__ENV.VITE_API_URL; +const buildTimeBase = import.meta.env?.VITE_API_URL; +const BASE_URL = runtimeBase || buildTimeBase || "http://localhost:8000"; export async function downloadSong(query) { - const response = await fetch(`${BASE_URL}/download?query=${encodeURIComponent(query)}`); - if (!response.ok) { - const error = await response.json(); - throw new Error(error.detail || "Unknown error"); + const res = await fetch(`${BASE_URL}/download?query=${encodeURIComponent(query)}`); + if (!res.ok) { + let detail = "Unknown error"; + try { detail = (await res.json()).detail || detail; } catch {} + throw new Error(detail); } - return await response.json(); + return res.json(); } export async function getDownloadedSongs() { - const response = await fetch(`${BASE_URL}/songs`); - if (!response.ok) throw new Error("Failed to fetch songs"); - return await response.json(); + const res = await fetch(`${BASE_URL}/songs`); + if (!res.ok) throw new Error("Failed to fetch songs"); + return res.json(); } \ No newline at end of file