From dfef34576a01ab29428ee5e5d99d325d93747a47 Mon Sep 17 00:00:00 2001 From: tigeren Date: Sat, 30 Aug 2025 19:00:46 +0000 Subject: [PATCH] refactor: update Dockerfile and deployment documentation - Modified Dockerfile to rebuild better-sqlite3 using npm as a fallback and ensure native bindings are compiled correctly. - Updated build command to 'buildprod' for production optimization. - Removed unnecessary package installations for the production image. - Enhanced permissions setup for media directories and ensured the database file is created at runtime. - Revised deployment guide to clarify environment configuration and directory setup for Docker. --- Dockerfile | 25 ++- docker/data/media.db | Bin 0 -> 118784 bytes docker/docker-compose.yml | 24 +-- docs/DEPLOYMENT_GUIDE.md | 389 +++++++++++++++++--------------------- package.json | 1 + src/db/index.ts | 11 +- 6 files changed, 204 insertions(+), 246 deletions(-) create mode 100644 docker/data/media.db diff --git a/Dockerfile b/Dockerfile index 6bb4081..88f208a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,16 +24,18 @@ RUN pnpm install COPY . . # Rebuild better-sqlite3 to ensure native bindings are compiled correctly -RUN pnpm rebuild better-sqlite3 +RUN pnpm rebuild better-sqlite3 || npm rebuild better-sqlite3 -# Ensure database file exists and has proper permissions -RUN touch /app/media.db && chmod 666 /app/media.db +# Verify native bindings are compiled +RUN find /app/node_modules -name "better_sqlite3.node" -type f + +# Database file will be created at runtime via docker-compose # Create directories for media storage RUN mkdir -p /app/data /app/media # Build the application -RUN pnpm build +RUN pnpm buildprod # Production image, copy all the files and run next FROM base AS runner @@ -42,11 +44,7 @@ WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 -# Install FFmpeg and FFprobe for thumbnail generation -RUN apt-get update && apt-get install -y \ - ffmpeg \ - sqlite3 \ - && rm -rf /var/lib/apt/lists/* +# No additional packages needed for production RUN groupadd --system --gid 1001 nodejs RUN useradd --system --uid 1001 --gid nodejs nextjs @@ -54,11 +52,18 @@ RUN useradd --system --uid 1001 --gid nodejs nextjs # Create media directories RUN mkdir -p /app/data /app/media +# Ensure directories have correct permissions +RUN chown -R nextjs:nodejs /app/data /app/media + # Copy built application COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static -COPY --from=builder /app/media.db ./media.db +# Copy node_modules to ensure native bindings are available +COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules + +# Rebuild native bindings for the production environment +RUN npm rebuild better-sqlite3 # Set up volume for persistent data VOLUME ["/app/data", "/app/media"] diff --git a/docker/data/media.db b/docker/data/media.db new file mode 100644 index 0000000000000000000000000000000000000000..647789c352b7a9e1362465914b110acf0d0e2f65 GIT binary patch literal 118784 zcmeHwd303AnQvd+tL3(O0b_iPG2vo^k&IhHU=!mRo5(^eMlwch!45;(QUe`p!CJ5t zZ@S&ucNw%H2?-$yA<%{plF*jrC7GF=GcV`l#d-N-GUp-H?R#hAmG(1P%*wp>VEb*sMm-KwvuzWSDf2coR07XD;Xa)LRP->O-rp(xF_cwVE??12B* z!2kC@diaI8{|Ed_P5FJMUw3Fi@}JNf|D;*w_n#W$cH>VB4&A>Rh5~!E;p}f%8~b#? zU_f+$kAJiOQ7Aw@1Ob8oL4Y7Y5Lg5RjxF=!wsO=**2Fl=$JV%yPb664tmYG`>1Qk_ z&X`kAD}OGJh=~k86v^*>duQagd6iQ9{j(|lf@>RnEWpIwAP<{9lFB0b>;n}qz@9z$sbHPep+ZRl?$voH{LQFHCv|*~mRCO3 zIG9g|B6ml|@bARzjSi1F#_x+f#)lt1w14m0@LY7{+lOAdSN497CZ2eE{~`YE!%+rK+oc#UHfAq_wIgM>Fy+md`#r7NN6P@ z^58vQ0&frlzB4i^5|~d!_`!(qoskM6!M0cZ{JFJjsmvV;;uG&=;un7xizL}f3zs<@=)ZrVeH-;zx?61_Z~PLdA|VW zZ@f!^i5z83z*+adNwI$4;@&{&>9mCR6V29mrIPz4@4tN~zqGuu|>+vS)n3G32i>6pUOtU1$TJAl0|4+tcD7a+?C7Kf~FG0~@ zfu)pLD43NxC+M5_#mA+diLW1OSj{+ioIr&6_;HbIX98lP=@VfSztXV13cy<|e5j|4 z|Ehr>@*xNi1PB5I0fGQQfFM8+AP5iy2m%BFf&f9_Yk|Nriq>olO-M`)1<|EBEHwIv z7WzdQ?`q(Od`X-RCPRox*AYU-M7GGM$Fq$A!|{0|ElJ zx(G=^9mNGw$qkd(E1-d!Aqi(*K5LD$Bu)Ev@4kl6Gx6qF%ju-}I7_lgDHW1(_SBZm zTV643e$}*fGat77wauGf+Y)Ab*J#kNwpTdYDrUwO$1?Jftp_#djAay{k~Xob6V)_0 zY`0Nu>-g>4j*+JE{&pTUU2xPEjNi^4A8r`$&v4WZ@gX50{4R64g+H1EmT`B7a#3+T zI^R8(A&50M?haiL>xz-k%u81)FKK}B|{^tzl=h(<1s_fM9*J5&h8P*a-> zbr$77NvU)g6%RTxFG<%b#M(O4R6dqr8_Ose%P5_W&{#&J;JA<}-M%J`3;-(d2F3@Q$NO)i(JH`CwD+LVUI6CJbpBXU zS~7nSSO_1q|Kv$)tkoRPD?^Jvg854DBz6l{J^gL_ue}FB7l=VP#R>aVL1-y)v}B^r zQQ#5KYsdN4@xhGofn1nc7?bkq@!=94IN10=sa#bL7@;N@T2~!;xngYw%sEtg{qB$e zkU0j2q`cemx9jha*IbrytaE(nBR>`i&1X2m{$OE&6x|FTkL5QYx5jeZzG`(eqh0woMri~BJgkE zL!Cu{jF?{m{AqHeEOMiC(hmo{22A*z#pCA~C%mo(m}I}SZ5y6LBZcE5*|Lx$wG@mG zR=9B%=<`?x@F|xvyjdJvF}mKus*STa}vPYhp!?bpC<~ zHC4!UjWAuu`zsvfg_DSSD)B~v#%nrkPy69vk-`Z_Rfk3MsGL)b@*7ZduCk(KC@b11 z@E55K_Cm#D_S`Xh+n8-|%$^B@1@=N>WwUdkhQSgf*9@ZJZXPxFjQ0;pSH46|-D1NK zOvyp{QX%YvrhvN`Eom}$ob?`>^}ZOIFE}Aq4I0@lpps5>z7yk8ZIdg`IkGzBnpR*m zyqr-jHr7dfo#Xxa_~i{Zzyv|f1Hfj0wMcDU(r6#fg~h+Jc#^wJ+vQB_-F4)+U%o;L9XXMRAWT)pk1!blxtvjh4&Rz_0mWeYPtff61Ea| zhifAuoKqdOSE7OPNg8MU*qIi+_awhf;dvR%33^o=T^g5q`(UFD6AcKCNIGjt=7Uga zk6ia9>gfvhwgiZ30 zhA!h4<0)f1u52DYf5iwpDA#lN1ebTdbi579I|qCYR!4_$4JP`SFfu0eI@2Fl^3mpG zt7X?t9)4B6s~odsjS1O!u)HAfj4fh%{o?g3RA+m68`O<*`^1Valy!Z4^pe<8>$sUA z7iOaD25GPe<=mEqbIM`5M7#kUqzZG5rVC zKVtrXJXq$qdESAKWF_AW+45?L*nSxe!P#v{x>N|8VNBr$*lpWaO{Y-0RV~-_iCtyV z^#;d{5n1TRv=&vuWJOoXP-~S*%q<1pWpb2XgN>zJQGjVXpJM`%0qVGALWPy%x3A;x z@P6s42^9(B19f8i4HGKOLszoprcyN6V?u4i@MD4#B^EX0LAkCH)Lj7olz|XLE^Lu6 z*Tb&9n1?f=SX$&fEJJrpd`x&uBp-eF;NA%7GBDIy+?n+uwAsBYf^E!}KPD8839ax_ z#_X_;;~!l7`3>}bx8hi|QF9YBVKPO0>{?es^}OTpp>%o~OTjFyS*GOPjpS5b30 zuyS1gx_Cx-&;8LsINsiZK_p+Uk$alamxWUOb=6HMEE4k7a%8&!AmYM8gL2-!d_xpE zm2rTNHQy#H}%uRUqH4boAapB^|IO$#7JUz(E{gF;Ayt9|oCC2_Mc!TjS%c zNs0VBNhvAT_pRXJ1baX+@e!w!ttsI3$ih3BH0mf6%WuM1i#~z{7x3aiaIt46?&(_L zImWizz4e?Xl+y(ep`i>Q7!X4xmrba+MXqc`S?w@@QE4s+j8IJ@v^L}SYPI0<9&5k= zSB;{gOVZ6cu^Cw=SnWl8;GBFoV086Fh7aP*Wq!3QYU6fMx)OhUm5;Kf;ej-g3oEQMg(1X8!J5FQC#CTzNqj}i;Yx9@&{8bXLxf8NF1QynQDDT zSjE_0aj$kNVlcD18|3y2Kr?ho8fue9Z{kT%)Fri6VX0AVD;5gW=An)p9?#(1F2EXw zJm3!O2?jeV8$zYP#IP)BLhNRmDpfMQV@(9_cTOQm3XqFCyy_4mY;vdU=6Td!insYN z42x<}Q45?Ic}H`u*lk0(%_zG_>d1$00bzVf+-FXWfCKi9&_P%Z6oF0wK9zQLOC9In%!>MNVZoZRiIi=Z9T%^|-W>0|b4x*Ni#Osf z&lcf+aKZt`zQL_ce5kDf-kt++0ajvNJ<7_%ohrE{R&5Jh&l)G z8|?szaUmM$ceIpX$pySSsCl^2k$=ni%BH@kd#~oHO|(0A6>D#btpm6R13f6`()d6D za2?$Jl{*`sXNHHwfz)+caF3nmAgYzzE0P zJHKFZU!4kngNeP6^4maxW^Al`4;NWpsr`Tvj=9mWP@+M03vG38>H#vofKWQt{Si-M zHvf+qA;tZH#gJn8ea;A9xHY_p(ji|#!`Nu|_7_v=!n{l;sJ8D(EJ8;zg5Lcp7g>Qf z@(rIDMhJFQ-w$<_l1RO9?p`&U#TV=UgVTTVAqWrz2m%BFf&f8)AV3fx2oMAa0t5kq z072kui~u?Re~n)*=^8K^fpO4y%h+waZoFb_ zG}albjOUES#yn%D@eAYkjcLX+#t)1qjb`I_j7Ny{lP7Sp94E_gV#J97CwiRda1w|UElyaR1mMIUCw@5b#R-EG8Yez9 z9l(@ch|(0Zi*nf|K2K%b!BuMg9&(EYpa*SY~+jqWqu z30=7Ed7W>dBk*qmuLTwbCIub{+#0x2`$z3>w1e6@?H5`zG(kQD0fGQQfFM8+AP5iy z2m)UZ1lBOzR_ZK-5lz~%dCM!N&99oaZsxcRbH^gJE$z$$I z9&gZm`GPAdk5lJ?6gPF?WN<-1Q!FpZA#ioX6aC9&>q*xobVyTW7c6CQJyd(2(tG1urZ*WfW%?=e^B zF*nd-uGV8N>oGULW3Io)TtAPwz8-TKkGZtRT%TuwKeIFOXCGLDDB~~iO7yhx7d}>> zDxVRbKhYNY4wKFN()Uf@pZl%z8}eW7pBZp8z#j0|EX`WkpRxa>&DQ>Xpgr(+x;UMz zKdjHy_Zz|tUB)fOQ^xjrTGsxG5q7x6OoRx#5Sv`AY<33nh1j}c-nsGqe28T35U%0S zyu!$s(CbWpT**h9ldYCrJ9+q3`L1%zmIa~aaoFD%1fH=)Os`+Oo`vdcFK>goQEs1D z(S@?EkB?pwTWTTTvs{>ovKyqqA_%1{3+Kcd2#s7K-pG>&t8mEcrVCP20-(oiRk@|!;(50*J@o`)FU5ZL#dAzNMz5!)}Lp%O>+kaVf=?vQ{f z+#u{@?5n0zDBY@-Yx=~lGUE6Uj#(KDakRx;#8S zT#GxiK7=;AcSW#`+49GP!ZD#0Udot#(D{RlKfi(A?^Ybgr-ta^Be|#}Pp(rceQrDb z(ezR{X!7}b7!ojAAnvo&Sp@h%kmdfXsJR?MV&nSP#WR9?gzE9ZK}Y5-9PSwcJ@+)D zFAJsm>uxcfp(Y6T4NtL4ac13<)u1qe?M@wzYH5QR=<9FzdA&IseUG5%9V_{5D) zlvN<#u5|R?KqVbK?ppPTRC*m{6rhq$2#L#-;O7=Cl*2vwin^rMDyeiB)waUqaMb3ZjvOA(;M^`8 zL%u%)Y612HgB_I(p;BOCC4do(UMD9pO${q=ddHfWZcXG}f}XEB#0Z<*JACszYA+QV zhVU>fszpUDFkN{^bFSEJL%Gc;yGZKDhi?JlqC3@X`^?Ff#5l7Re&8>R;?pYevC&AW zRFT6&l@KT$`hqgbpo6d+pwZs3i~?u@b#+S}=f(#sQ2#Bdw+nE90;@sV# zLV(<1yFKmMBHSb=9ANAl+&Y5!Ne%G!9Dob366@+wRvzwD$t|&>Q|#-(>qzA-xe{Y7 z<`rNTVjFO#VsR@fEgQd`4a`o=9fjtl;SMx-4i+sZWn$|kv7t{Pt4u7v2uwh1Y;ma} zt5M8qM4bcpjds8ySBM7sAxyZa%oAMmaHAvt*7T_KFp_YWo|;Vv;+>2T=-zvnfOXYU z;Q;j5F<33uwuyDk&`~L;4fS>NK`6gf%n+Q+U77SxzDB33)lmy|m|z}3QJC66&NW%= z&l0P<@XCy7U14E_X!mE^n_vln(J7YH010y4m#`pUPTzh(83o1Ctt=R+Vr7F^R6nuu zxvdp0gY}8yjPSZ!D~{pZNvEF=cAg7V)iYuUEHC|s!Q&2;`)Qd%;95coMC zG{MdlG@OfsGTL=2{0%0yqa_n{jylL{uik-hGJ-m)t4eU=!i&1Vi@HfEPnG54i>}kUjUc0W3a+)8bdXh!@HK@^hoxfm0O%-xoBj7&X zU*RY(#7+2r%m^v&4>XU;ImIZy0X63;gF8bR+@tV5r8bz<#bfr|F?-vX4fffY&`IY> z3e$1*B6KoHt{Ftb-8^dUfz5_=st(fLl?YPAhk zJV#cCT+<3~l$SHA#l|||V{l5tJs55P*iiETUVLivq_!?;v=8UPEUPS@e2oR=Vpf5Z zG#5taD`*%S?cRP)nN(bi8_%qPHstp6Vh>EbVrT}IYUn#|wH6L(7tv+lM1`<_!{Zvp zadQu_TR1A=Q-!S$mdgiIz`RT+sJ3sK+IsX6=f)h~FMeHgBqQkEpBl8wwV5*P=uCLt zRnA(jc=jfbAI3T`=S3|_}c@Pffm3JE3ozKue3heceQQW z9|T4RR_H=>#k$|?tzaSW&jy1b-SCgbXzxIeTcyl+Y7_hT7qT0#Q+YT|b#C!LB;N=!+G zzg#XIOirnkA`*KM#4;PHL#HipEo-(WrX(bNU`bBlU*IE>;^WT5o7179gK&$OdfEz7 z^0(4bQsF-E0}FrXv^9kf;$z_!FqwZpoqsE2SG<{5stgt=#^y_zf$I!f(W>)?DhG>&S9K_&}= zP!QlcK(d?#cWoJRR-dDw60TK2!NHDi9GYr*(BIn<5(fW)uqnuSU639NAE-kd90ifD zQ%Z#eu_ahE4DU;Bssa%jekWOZGQL#7qBW4$qP!wSlL2nN@O>G|>Xv$M!nFZvxCtfU zdIKI2E3=@C>Kig0v0747(%x6S_}lmnBRo${NHFI^7vu@Xwh`C3l(auyXKbHgnjTMm z51x*s#yn|waP(FR-y^x}bYSTve2=fb9;n5ks0b~b-iD${u!x!(PcpVm6jIZwc!atc zuhU;p=%JM{zRL&^6k0&_i*!av!woFHzQt}vSUy9`v(vB$WNfP_;I7aGxTe5bD8O$Y zkxs=fMle!n&RP_4jIfVdaJ`8L#?Dgs8tyUHlvqRWpv2mnVk_2lKm$D}=Mt>O!2R$d zyz&(~i4te64Dk|u-1(F@>JLX5A&e5|U*TatBdnw5QN>{;9eS@i7o`Se@+=WGhCPh1 zLZNZaiVIIMHt6k=Q&l*@2;mgoLN1VUg2FZyS{)&c5fUiJV(T9$`acV%aj>3_ra+z3 zvyQk5&pKU%@X%qcx=0&F7H^evJZUF={&WG;r6RNq;|&I!EhhVP}Zw+SF$e$>hmg)FWgy7@PRfzQEPJ%4`@NY z9O%-k^n0lt+F}5#)vAO(^hquisyv}uitnzJT|Dc{Fomr!x&DLLC<{Dd<)(|pehox zK7W3T;nHWU$(u9j2S7`-Y7O&$PK|}OURP^t9?!1947YFQCS1<5Gjf3?a>o4*z!f@E zU*>%7eGzz|I#NsSESv&B99IQm;m*f1^2vohGYc%d7*@5ni+zy(5O6=HM#+L5u3u$1 zt7?M}I&iG#1Dt+mGih%>!H47lvZO5HGHaA2QgMu9d)H{tu(nq?+bU+p79X`?-jr@WYEpdu{1)dmxWXX= zfGdP@!it%|o#R$z0+~71!+h|EZC-cFc1DlK%s$Q*s@kokwnuV;wA84g{&23+d&qrs zcqAR?gm6{J7i_QR#BRZ=r@w9gwf7+C0x<}uIPgfK#;X?Ae$7*YHdJUNHIg7{7F*s5P2jyujCEk;Y4%pGUFhi1JmhUN=Sh*g7T5l> z%%h?csKsni^G8#z*H8(|XqfZ~+<}>a@^qS=(mPMZGoYBS?@4}}!t*j1PWGxex-<^@>fl2JTpNH&c7&qk9Ryydz?lr{=?Yf0 z@K@=<>?_ffZO!t!L-_n})QoA2s|_#cb97(nj%xGSPXey`NBMo^`v&t(I?cxqvLEM< zjbDr3*(Y#)srJqm?fO`Gxr^2sz4q*nydDL%_-|l7x|_3YQiW(~sU$mrTv{|yPkv_b z8g6078qD}sb6=>U{jgW`PFx=LX&=K|pL*Q`8}lrlQztkfLbVGErxM`o?rDf8{lBLd-LG{Fpdlx%a@$MxDX~46~_5bNPSO zdfh48yQ}bMm`S9&G&KC3z$^)k5~{S+;x)L!{-^PX*~AG(wcX;JO%|NE%vF;mG47bx zJ+kRI+@qPKY71;V;q33is@G7zS{>yz9QL=K0yyj&I6JGx5*}}T90l#iy9@N^x3+lw zGCx}hht2o69coi`MinF{NHf{Ooc`;-_qur@*flbH`POK5p3ywW&!my(p~thq$-AColO4%&MX}AxxFPdDqHvc4gkH^|Af%Gw(b7 z{&L*u*EwOGTBqkvJxuBB{8gvLqKcaX(6r{rC0D#dw1_S#2m#lK@tNwFlJ0vmknvsCfLKk37ge{avu zc#_V}x_N!xda41>` zVHC!M4)FU4DY&@=|ETv~Lwn(!jJh$~z?f}x%-(|^u{Dm_+sEuf@F!&P;LyQQ-y^ky z58+&~xdFVI%C`rlOP9d~!bJa;03AMi8g<^on!E|a8qL{MS?JR9i=j-5rAHR^-8dbLyAi<&8`ZioudYXwj?HV0KEEno z>vdOcyMpjES;yIY)w=p9J)$VcJW2xm+K#;JJ&i##xx1}V@S4Z^e5lm_@Vc9}(;M*kir{RjJPF8JI=*!7 z1GRK0^8fY8HC{t)e`7sBZC}ONKAT}gJ(ezl?!T4ClK72pjKARZ3lx~=Vdrk>xIFaNiH@S2R|=YWd++*UOzRIG%R##O%8H2uviRKK2C2aSHjy*ERn-k2NT zC&Zhw%yGYWyavRk;W4Xzj}vyNlDAL+!JHjhsM_hDq;z==fi^&^S)=)phW#&Xg!Y{F zZlDm@qT8waoqm_T-LTW}d*in?KcdR1zn$4L0I~m{_gHzv{{LQxnaP+cJhA_uDI_0A zVWtJtBliDFT&MXCu}AFx)$G~keE0hFjCQRHdHhWb6R|DO_CY5s0x zmM>BJ|46VZrPOBh6wk#+tY^)9l$k#QK{BVrm4iPmNs6!Hu>DZWO0`;chVS3EAIDyH zboYoin;pfOsQ()H44xXZ8G_>}k%IXndv`|e$I|`HGcws-A$vn&LU4Fql=Y?9YD2YE zu=c^Nx-%dr#NXnBlI}%Eofa2Yp*2)k24=;a8nKV)0w`oX!-cETGv)rqBs>dbt9o5n z0@olf37ddARd*dr@A_cVAsDS|)c~J^F#pAqP-%o(rSmFC4;EGe(CgINd6Z)5Ai)G= z9Z(}{DGGBp#=*}&YpI)0Y^2@YHBorQGtblU+G@b_3M7EPcl4Zuo!-7JPH~A zLDl+LeX4v$eEvjR=sQd{^Gn}1eShw^&Tq(nxqqfEPABUR>vQ$}hA=~yaf|Vkv0Z&r zFzauX;9jo#hk2FI@C*oOrq#c6bi_7*w=D>4Fpa{;QeSF)2yJ%niU+->=Qq&%-HNMd zB0aaA{%CqBZ`6d=#WN7Vz!lnsi(T+31Ca|{8(TC#5Nn0!7>HTm*80+Np74z*2BHEO)Kx2{20&p!2?Fd|-8?Hs z7#YxuaBbnyszhvLKqJDHpe3mcf$#sH46tjAgT@TwGln5UmHxlj2SRi3)Z@62IX>KgHR_uO?hX}-wRuuomo(akb8ly(%HpvM8))pqH4G{! z7qbe)_Co3E#i;aTYb-chD#Pmgz8^CXz09@w#fc~$O2@jl>nT!!{}$TnUj6|=76LwC zAoQGD-|UyRZ39@*Na6TMwk+gGEd}F)6>bm#IM^eSYZ+dyZo_iEs%FQP0+f9nH370k zcdFYkjx4yWfrxvqjV*fAdKGEtLSOYKFc4PJt=kJ021mEZBBI;bQt2=%9&}`0lCD*V zwRNbe{K0cdzfkhARSZNNcEh^3Kq>)eUHQn?gPL=|g&Hbp6RSE=O@qUB8`Xk8``eC@ zCh%dxqoxav+5%tSi8>{hjJ-raKvpc=Q{+5oj)~NOf_JKcZJZkCTuOR>iCig_;{KIHx% zMi06FFGK_Vu<;iQn&C`{kAlOEj{IBGkAn9Of#m+*{a8%y|2^&Y7wUXN?*HS=sphay zSOUrYKe_*R)Hln*xd+P|Hj(>(xupW+7Fe!VGb~>&mg{@Q2TC0vhsx?7%gB(k`Wyw7 zyjanNvh0eV`dS`hyl)8!ga2UH6a3(6dwMIgZw^DE%IgU>Nqa{mwd8Hy`mjAMC; z*xoPJ)`Qv%>gq=J5(w`u^>$%-Xpvniy)Nf~ipRyNC1El{F^EtmgV{+@4wNMK|K$FE zQd{bYM}PmnOtVV!J&oaIu=sB-R(dzwozfeYEdt+q6Fjj1H{Oh3JZPzt>yAci=x842E>WKN^o4GmV2&cPs+O z*utnKXzcPWMxd#=n76GAaUz|)b`m?sYp9KDrN)9KhmzlXo~%8P^IB4pwGHYlD17SU6~ zlZg_f9ahS%vYDD=?I7!=dLg;c*tXM{8g8d!XP3%eO%`3x=3PQxOQv8|%e((tLE z(fF;W<{r=?juG}z3$8a2!Pr^qG53_PhTcKV7!iHtD|8Yi&e|EmOZ0K)Q{K2gILZiN zlsNwm5B4*{I%*zW9IT{6?^WmG)Iga$OT>-A9!6N9&^Twu1&aB)W8qvCJmfj_1S5n~ z_!jis4SY2U%HWEaO1gbb8W~VD%gV+Fo5%ZaqtPlj>x%XsG};Rig*VgrV@YYr{6SDQ zn-WAqF&uH~nknHKbPC&8Xaj>ZMo6F*-I794R+kpaSY$mNO-T>3URg))raX(pzQ+hh zsRvr?Y^E*LtQ=e5L+(R*vQQS;dC*-^TyQ-qXBErn4b*NI_Y?t)Z3VRy6jHGLKLyX6ek~o06~Bt zKoB4Z5CjMU?g)TwqMn_r75_H+L&~)dugvyepbt>;gAMhSt4(J)ILp8#AKbUg?dQcF zFvu+i`*#Q`C$-J*&HNcgh^6F3w8i&sKR;&A9kaKM*#^h#nG;fVr)fW&-GbzrK{VXW zgD%2&|Dbf`OVrdYHVpCbhkU6J%zBlho8N;E7hauF7WSC*XpVl1a;jbEqq(p|MH2HT zb6HA~Bz0lee2df;O7u4?`QyWhI6aF^g|CBTcu{2NdRVz>{m(SUO@<`+hkOVE1Ob8o zL4Y7Y5FiK;1PB5I0fGQQU}+;DYzWh9Sb^d!%=UnQfUT}R{bqAne->0Ul4kRO&&sZi zp=aXFv6j%_SL literal 0 HcmV?d00001 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index f54f403..21ef6d5 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -10,14 +10,14 @@ services: volumes: - ${DB_PATH:-./data}:/app/data - ${MEDIA_PATH:-./media}:/app/media + - /mnt/data1:/mnt/data1 + command: node server.js environment: - NODE_ENV=production - - DATABASE_URL=${DATABASE_URL:-file:///app/data/nextav.db} - NEXT_PUBLIC_MEDIA_ROOT=${NEXT_PUBLIC_MEDIA_ROOT:-/app/media} - - NEXTAUTH_SECRET=${NEXTAUTH_SECRET} - - NEXTAUTH_URL=${NEXTAUTH_URL} + - DB_FILE=/app/data/media.db healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] + test: ["CMD-SHELL", "node -e \"require('http').get('http://localhost:3000/api/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })\""] interval: 30s timeout: 10s retries: 3 @@ -25,21 +25,7 @@ services: networks: - nextav-network - nginx: - image: nginx:alpine - container_name: nextav-nginx - restart: unless-stopped - ports: - - "${HTTP_PORT:-80}:80" - - "${HTTPS_PORT:-443}:443" - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf:ro - - ${SSL_CERT_PATH:-./ssl/cert.pem}:/etc/nginx/ssl/cert.pem:ro - - ${SSL_KEY_PATH:-./ssl/key.pem}:/etc/nginx/ssl/key.pem:ro - depends_on: - - nextav - networks: - - nextav-network + networks: nextav-network: diff --git a/docs/DEPLOYMENT_GUIDE.md b/docs/DEPLOYMENT_GUIDE.md index 693a001..d0234d7 100644 --- a/docs/DEPLOYMENT_GUIDE.md +++ b/docs/DEPLOYMENT_GUIDE.md @@ -1,261 +1,218 @@ # NextAV Deployment Guide ## Overview -This guide covers deploying NextAV to a private Docker registry and production server. +NextAV is a Next.js application that provides media library management with SQLite database storage. This guide covers Docker deployment, troubleshooting, and best practices for the NextAV application. ## Prerequisites -- Docker & Docker Compose installed -- Access to private registry (e.g., 192.168.2.212:3000) -- SSL certificates for HTTPS (optional for local deployment) +- Docker and Docker Compose installed +- At least 4GB of available disk space +- Port 3000 available ## Quick Start -### 1. Build & Push to Private Registry - +### 1. Clone and Setup ```bash -# Build the image -docker build -t 192.168.2.212:3000/tigeren/nextav:latest . - -# Push to private registry -docker push 192.168.2.212:3000/tigeren/nextav:latest - -# Verify push -curl http://192.168.2.212:3000/v2/_catalog +git clone +cd nextav ``` -### 2. Deploy to Production Server - +### 2. Environment Configuration +Create a `.env` file in the `docker` directory: ```bash -# Copy deployment files to server -scp -r docker/ user@server:/path/to/nextav/ - -# SSH to server -ssh user@server -cd /path/to/nextav/docker/ - -# Configure environment +cd docker cp .env.example .env -# Edit .env with your settings - -# Deploy -docker-compose up -d +# Edit .env with your configuration ``` -## Detailed Deployment Steps - -### Local Development +### 3. Create Required Directories ```bash -# Build locally -docker build -t nextav:dev . - -# Run locally -docker-compose -f docker-compose.yml up -d +mkdir -p data media +chmod 755 data media ``` -### Production with Private Registry - -#### Step 1: Configure Private Registry Access +### 4. Build and Run ```bash -# Add insecure registry to Docker daemon -echo '{ "insecure-registries": ["192.168.2.212:3000"] }' | \ - sudo tee /etc/docker/daemon.json -sudo systemctl restart docker +# Build the Docker image +docker build -t nextav:latest .. + +# Start the application +docker compose up -d ``` -#### Step 2: Build & Tag +### 5. Verify Deployment ```bash -# Build with registry tag -docker build -t 192.168.2.212:3000/tigeren/nextav:latest . -docker build -t 192.168.2.212:3000/tigeren/nextav:v1.0.0 . -``` +# Check container status +docker compose ps -#### Step 3: Push to Registry -```bash -# Push latest -docker push 192.168.2.212:3000/tigeren/nextav:latest - -# Push versioned -docker push 192.168.2.212:3000/tigeren/nextav:v1.0.0 -``` - -#### Step 4: Deploy on Target Server - -**On production server:** -```bash -# Create deployment directory -mkdir -p /opt/nextav -cd /opt/nextav - -# Copy deployment files -cp docker/docker-compose.yml . -cp docker/.env.example .env - -# Create SSL directory (optional) -mkdir -p ssl -# Copy your SSL certificates to ssl/cert.pem and ssl/key.pem - -# Configure environment -nano .env -``` - -**Edit .env file:** -```bash -REGISTRY_URL=192.168.2.212:3000 -IMAGE_NAME=tigeren/nextav -IMAGE_TAG=latest - -# Set your domain -NEXTAUTH_URL=https://your-domain.com -NEXTAUTH_SECRET=your-secure-secret - -# Adjust paths if needed -DB_PATH=./data -MEDIA_PATH=./media -``` - -**Deploy:** -```bash -# Pull and deploy -docker-compose pull -docker-compose up -d - -# Check status -docker-compose ps -docker-compose logs -f -``` - -## Environment Variables - -| Variable | Description | Default | -|----------|-------------|---------| -| `REGISTRY_URL` | Private registry URL | 192.168.2.212:3000 | -| `IMAGE_NAME` | Image name | tigeren/nextav | -| `IMAGE_TAG` | Image tag | latest | -| `NEXT_PUBLIC_MEDIA_ROOT` | Media directory | /app/media | -| `DATABASE_URL` | Database file path | file:///app/data/nextav.db | -| `NEXTAUTH_SECRET` | Auth secret | required | -| `NEXTAUTH_URL` | Application URL | required | -| `SSL_CERT_PATH` | SSL certificate path | ./ssl/cert.pem | -| `SSL_KEY_PATH` | SSL private key path | ./ssl/key.pem | - -## Directory Structure - -``` -docker/ -├── .env.example # Environment template -├── docker-compose.yml # Production compose -├── nginx.conf # Nginx configuration -└── ssl/ # SSL certificates (optional) -``` - -## SSL Setup (Production) - -### Using Let's Encrypt -```bash -# Install certbot -sudo apt install certbot - -# Generate certificates -sudo certbot certonly --standalone -d your-domain.com - -# Copy certificates -cp /etc/letsencrypt/live/your-domain.com/fullchain.pem ssl/cert.pem -cp /etc/letsencrypt/live/your-domain.com/privkey.pem ssl/key.pem -``` - -### Using Self-Signed (Development) -```bash -# Generate self-signed certificates -openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout ssl/key.pem -out ssl/cert.pem -``` - -## Monitoring & Maintenance - -### Health Checks -```bash -# Check application health +# Test health endpoint curl http://localhost:3000/api/health -# Check nginx health -curl http://localhost/health +# Access the application +open http://localhost:3000 ``` -### Logs -```bash -# View all logs -docker-compose logs -f +## Configuration -# View specific service logs -docker-compose logs -f nextav -docker-compose logs -f nginx -``` +### Environment Variables +| Variable | Default | Description | +|----------|---------|-------------| +| `DB_PATH` | `./data` | Path to database storage directory | +| `MEDIA_PATH` | `./media` | Path to media files directory | +| `DB_FILE` | `/app/data/media.db` | Database file path inside container | +| `NEXT_PUBLIC_MEDIA_ROOT` | `/app/media` | Media root path for the application | -### Updates -```bash -# Update to latest version -docker-compose pull -docker-compose up -d - -# Update to specific version -# Edit .env: IMAGE_TAG=v1.0.1 -docker-compose up -d -``` - -### Backup -```bash -# Backup database and media -tar -czf backup-$(date +%Y%m%d).tar.gz data/ media/ -``` +### Volume Mounts +- `./data:/app/data` - Database and application data +- `./media:/app/media` - Media files storage ## Troubleshooting -### Common Issues +### Database Access Issues +**Problem**: `SqliteError: unable to open database file` -**Registry connection failed:** +**Solution**: +1. Ensure data directory exists and has correct permissions: + ```bash + mkdir -p data + chmod 755 data + ``` + +2. Fix container permissions: + ```bash + docker exec -u root nextav-app chown -R nextjs:nodejs /app/data /app/media + ``` + +3. Restart the container: + ```bash + docker compose restart nextav + ``` + +### Native Module Issues +**Problem**: `Could not locate the bindings file. Tried: ... better_sqlite3.node` + +**Solution**: +1. Rebuild the Docker image: + ```bash + docker build --no-cache -t nextav:latest .. + ``` + +2. Ensure build dependencies are available in Dockerfile + +### Container Health Issues +**Problem**: Container shows as "unhealthy" + +**Solution**: +1. Check logs: + ```bash + docker compose logs nextav + ``` + +2. Test health endpoint manually: + ```bash + curl http://localhost:3000/api/health + ``` + +3. Verify database connectivity and permissions + +### Build Performance Issues +**Problem**: Docker build takes too long + +**Solutions**: +1. Use multi-stage builds (already implemented) +2. Remove unnecessary packages from production stage +3. Use `.dockerignore` to exclude unnecessary files +4. Consider using build cache + +## Best Practices + +### Security +- Run container as non-root user (implemented) +- Use specific image tags instead of `latest` +- Regularly update base images +- Scan images for vulnerabilities + +### Performance +- Use multi-stage builds to reduce image size +- Implement proper caching strategies +- Monitor resource usage +- Use health checks for reliability + +### Data Persistence +- Use named volumes for production +- Regular database backups +- Monitor disk space usage +- Implement proper backup strategies + +### Monitoring +- Health check endpoint: `/api/health` +- Application logs: `docker compose logs nextav` +- Resource monitoring: `docker stats` + +## Production Deployment + +### 1. Environment Setup ```bash -# Check registry accessibility -curl http://192.168.2.212:3000/v2/_catalog - -# Check Docker daemon configuration -cat /etc/docker/daemon.json +# Production environment variables +NODE_ENV=production +DB_PATH=/var/lib/nextav/data +MEDIA_PATH=/var/lib/nextav/media ``` -**Permission issues:** +### 2. Reverse Proxy (Optional) +Configure Nginx or Traefik for SSL termination and load balancing. + +### 3. Backup Strategy ```bash -# Fix file permissions -sudo chown -R $USER:$USER data/ media/ +# Database backup +docker exec nextav-app sqlite3 /app/data/media.db ".backup /app/data/backup.db" + +# Volume backup +docker run --rm -v nextav_data:/data -v $(pwd):/backup alpine tar czf /backup/nextav_data.tar.gz -C /data . ``` -**Port conflicts:** +### 4. Monitoring +- Set up log aggregation +- Monitor container health +- Track resource usage +- Set up alerts for failures + +## Maintenance + +### Regular Tasks +1. **Update Dependencies**: Monthly security updates +2. **Database Maintenance**: Regular backups and optimization +3. **Log Rotation**: Prevent disk space issues +4. **Image Updates**: Keep base images current + +### Troubleshooting Commands ```bash -# Check port usage -sudo netstat -tulpn | grep :3000 +# View logs +docker compose logs -f nextav + +# Access container shell +docker exec -it nextav-app sh + +# Check resource usage +docker stats nextav-app + +# Restart services +docker compose restart nextav + +# Update and rebuild +docker compose down +docker build -t nextav:latest .. +docker compose up -d ``` -### Debug Mode -```bash -# Run in debug mode -docker-compose up -# or -docker-compose logs -f nextav -``` +## Support -## One-Click Deployment +For issues not covered in this guide: +1. Check application logs +2. Review Docker documentation +3. Consult Next.js deployment guides +4. Open an issue in the project repository -Use the provided deployment script: -```bash -# Make executable -chmod +x deploy.sh +--- -# Run deployment -./deploy.sh -``` - -## Security Notes - -- Change default passwords and secrets -- Use HTTPS in production -- Regularly update images -- Monitor logs for suspicious activity -- Backup database regularly \ No newline at end of file +**Last Updated**: August 30, 2025 +**Version**: 1.0.0 \ No newline at end of file diff --git a/package.json b/package.json index 1d3c438..cdd8beb 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "scripts": { "dev": "next dev --turbopack", "build": "next build --turbopack", + "buildprod": "next build", "start": "next start" }, "dependencies": { diff --git a/src/db/index.ts b/src/db/index.ts index 9f76068..8f18705 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -1,13 +1,22 @@ import Database, { Database as DatabaseType } from 'better-sqlite3'; import path from 'path'; +import fs from 'fs'; let db: DatabaseType | null = null; function initializeDatabase() { if (db) return db; - const dbPath = path.join(process.cwd(), 'media.db'); + // const dbPath = process.env.DB_FILE || path.join(process.cwd(), 'media.db'); + const dbPath = path.join(process.cwd(), 'data', 'media.db'); + + // Ensure the data directory exists + const dataDir = path.dirname(dbPath); + if (!fs.existsSync(dataDir)) { + fs.mkdirSync(dataDir, { recursive: true }); + } + db = new Database(dbPath); // Create tables