diff --git a/Dockerfile b/Dockerfile index f41bd16..b6bd0cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -86,7 +86,6 @@ EXPOSE 3002 ENV NODE_ENV=production \ DB_FILE="db/production.sqlite3" \ PORT=3002 \ - TUDUDI_INTERNAL_SSL_ENABLED=false \ TUDUDI_ALLOWED_ORIGINS="http://localhost:8080,http://localhost:3002,http://127.0.0.1:8080,http://127.0.0.1:3002" \ TUDUDI_SESSION_SECRET="" \ TUDUDI_USER_EMAIL="" \ diff --git a/README.md b/README.md index 5685cf3..14ef48e 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,6 @@ The following environment variables are used to configure tududi: #### Optional Variables: - `PUID`, `GUID` - Run with specified user and group ID (instead of defaults 1001/1001) -- `TUDUDI_INTERNAL_SSL_ENABLED` - Set to 'true' if using HTTPS internally (default: false) - `TUDUDI_ALLOWED_ORIGINS` - Controls CORS access for different deployment scenarios: - Not set: Only allows localhost origins - Specific domains: `https://tududi.com,http://localhost:3002` @@ -107,7 +106,6 @@ The following environment variables are used to configure tududi: export TUDUDI_USER_EMAIL=dev@local.test export TUDUDI_USER_PASSWORD=devpassword123 export TUDUDI_SESSION_SECRET=$(openssl rand -hex 64) -export TUDUDI_INTERNAL_SSL_ENABLED=false # TUDUDI_ALLOWED_ORIGINS not set - defaults to localhost only ``` @@ -116,7 +114,6 @@ export TUDUDI_INTERNAL_SSL_ENABLED=false export TUDUDI_USER_EMAIL=admin@yourdomain.com export TUDUDI_USER_PASSWORD=your-secure-password-here export TUDUDI_SESSION_SECRET=$(openssl rand -hex 64) -export TUDUDI_INTERNAL_SSL_ENABLED=true export TUDUDI_ALLOWED_ORIGINS=https://tududi.yourdomain.com ``` @@ -127,7 +124,6 @@ docker run \ -e TUDUDI_USER_EMAIL=myemail@example.com \ -e TUDUDI_USER_PASSWORD=mysecurepassword \ -e TUDUDI_SESSION_SECRET=$(openssl rand -hex 64) \ - -e TUDUDI_INTERNAL_SSL_ENABLED=false \ -e TUDUDI_ALLOWED_ORIGINS=https://tududi,http://tududi:3002 \ -e PUID=1001 \ -e GUID=1001 \ @@ -254,22 +250,6 @@ To install `tududi`, follow these steps: npm install ``` -### 🔒 SSL Setup (Optional) - -For HTTPS support, create SSL certificates: - -1. Create and enter the directory: - ```bash - mkdir backend/certs - cd backend/certs - ``` -2. Create the key and cert: - ```bash - openssl genrsa -out server.key 2048 - openssl req -new -x509 -key server.key -out server.crt -days 365 - cd ../.. - ``` - ### 📂 Database Setup The database will be automatically initialized when you start the Express backend. For manual database operations: diff --git a/backend/app.js b/backend/app.js index 504527e..3a0cfcf 100644 --- a/backend/app.js +++ b/backend/app.js @@ -23,9 +23,9 @@ const sessionStore = new SequelizeStore({ // Middlewares app.use( helmet({ - hsts: config.sslEnabled, // Only enable HSTS when SSL is enabled - forceHTTPS: config.sslEnabled, // Only force HTTPS when SSL is enabled - contentSecurityPolicy: false, // Disable CSP for now to avoid conflicts + hsts: false, + forceHTTPS: false, + contentSecurityPolicy: false, }) ); app.use(compression()); @@ -61,9 +61,9 @@ app.use( saveUninitialized: false, cookie: { httpOnly: true, - secure: config.sslEnabled, + secure: false, maxAge: 2592000000, // 30 days - sameSite: config.sslEnabled ? 'none' : 'lax', + sameSite: 'lax', }, }) ); diff --git a/backend/cmd/start.sh b/backend/cmd/start.sh index c9da6b0..2379d85 100755 --- a/backend/cmd/start.sh +++ b/backend/cmd/start.sh @@ -47,6 +47,4 @@ if [ -n "${TUDUDI_USER_EMAIL:-}" ] && [ -n "${TUDUDI_USER_PASSWORD:-}" ]; then node -e "const{User}=require(\"./models\");const bcrypt=require(\"bcrypt\");(async()=>{try{const[u,c]=await User.findOrCreate({where:{email:process.env.TUDUDI_USER_EMAIL},defaults:{email:process.env.TUDUDI_USER_EMAIL,password_digest:await bcrypt.hash(process.env.TUDUDI_USER_PASSWORD,10)}});console.log(c?\"✅ User created\":\"â„šī¸ User exists\");process.exit(0)}catch(e){console.error(\"❌\",e.message);process.exit(1)}})();" || exit 1 fi -[ "${TUDUDI_INTERNAL_SSL_ENABLED:-}" = "true" ] && [ ! -f "certs/server.crt" ] && openssl req -x509 -newkey rsa:2048 -keyout certs/server.key -out certs/server.crt -days 365 -nodes -subj "/CN=localhost" 2>/dev/null || true - exec node app.js diff --git a/backend/config/config.js b/backend/config/config.js index 86af874..038b2ef 100644 --- a/backend/config/config.js +++ b/backend/config/config.js @@ -65,9 +65,6 @@ const config = { credentials, - sslEnabled: - production && process.env.TUDUDI_INTERNAL_SSL_ENABLED === 'true', - uploadPath: process.env.TUDUDI_UPLOAD_PATH || path.join(projectRootPath, 'uploads'), }; diff --git a/docker-compose.yml b/docker-compose.yml index edc0e4b..2d01a3a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,6 @@ services: - TUDUDI_USER_EMAIL=admin@example.com - TUDUDI_USER_PASSWORD=your-secure-password - TUDUDI_SESSION_SECRET=changeme-please-use-openssl - - TUDUDI_INTERNAL_SSL_ENABLED=false - TUDUDI_ALLOWED_ORIGINS=http://localhost:3002 - TUDUDI_UPLOAD_PATH="/app/backend/uploads" # Runtime UID/GID configuration - set these to match your host user/group diff --git a/index.html b/index.html index 468605d..2671945 100644 --- a/index.html +++ b/index.html @@ -10,7 +10,7 @@ gtag('config', 'G-VC2N7ZBPEE'); - + tududi - Self-Hosted Task & Project Management @@ -27,7 +27,7 @@ --light-color: #f8fafc; --dark-color: #0f172a; --success-color: #059669; - + --bg-color: #ffffff; --text-color: #1e293b; --text-muted: #64748b; @@ -40,7 +40,7 @@ --gradient-elegant: linear-gradient(135deg, #f0b575 0%, #e0a465 100%); --gradient-surface: linear-gradient(145deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%); } - + [data-theme="dark"] { --primary-color: #3b82f6; --primary-hover: #2563eb; @@ -49,7 +49,7 @@ --light-color: #1e293b; --dark-color: #f1f5f9; --success-color: #10b981; - + --bg-color: #0f172a; --text-color: #f1f5f9; --text-muted: #94a3b8; @@ -62,17 +62,17 @@ --gradient-elegant: linear-gradient(135deg, #f0b575 0%, #e0a465 100%); --gradient-surface: linear-gradient(145deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%); } - + * { margin: 0; padding: 0; box-sizing: border-box; } - + html { scroll-behavior: smooth; } - + @keyframes fadeInUp { from { opacity: 0; @@ -83,7 +83,7 @@ transform: translateY(0); } } - + @keyframes float { 0%, 100% { transform: translateY(0); @@ -92,7 +92,7 @@ transform: translateY(-10px); } } - + body { line-height: 1.7; color: var(--text-color); @@ -103,14 +103,14 @@ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } - + .container { width: 100%; max-width: 1200px; margin: 0 auto; padding: 0 15px; } - + /* Header */ header { background: rgba(255, 255, 255, 0.95); @@ -123,12 +123,12 @@ z-index: 1000; transition: all 0.3s ease; } - + [data-theme="dark"] header { background: rgba(15, 23, 42, 0.95); border-bottom: 1px solid rgba(255, 255, 255, 0.08); } - + header::before { content: ''; position: absolute; @@ -139,14 +139,14 @@ background: var(--gradient-primary); opacity: 0.3; } - + nav { display: flex; justify-content: space-between; align-items: center; padding: 20px 0; } - + .logo { display: flex; align-items: center; @@ -156,40 +156,40 @@ text-decoration: none; transition: all 0.3s ease; } - + [data-theme="dark"] .logo { color: white; } - + .logo:hover { transform: translateY(-1px); } - + .logo-icon { margin-right: 8px; display: flex; align-items: center; justify-content: center; } - + .logo-icon svg { color: #1e293b; } - + [data-theme="dark"] .logo-icon svg { color: white; } - + .nav-links { display: flex; list-style: none; align-items: center; } - + .nav-links li { margin-left: 8px; } - + .nav-links a { text-decoration: none; color: var(--text-color); @@ -199,7 +199,7 @@ padding: 8px 16px; border-radius: 8px; } - + .nav-links a::before { content: ''; position: absolute; @@ -211,41 +211,41 @@ transition: all 0.3s ease; transform: translateX(-50%); } - + .nav-links a:hover { color: var(--primary-color); background: rgba(37, 99, 235, 0.05); } - + .nav-links a:hover::before { width: 100%; } - + .theme-switch { margin-left: 8px; display: flex; align-items: center; } - + .theme-switch-label { margin-right: 10px; font-size: 0.9rem; color: var(--text-color); } - + .switch { position: relative; display: inline-block; width: 50px; height: 24px; } - + .switch input { opacity: 0; width: 0; height: 0; } - + .slider { position: absolute; cursor: pointer; @@ -257,7 +257,7 @@ transition: .4s; border-radius: 24px; } - + .slider:before { position: absolute; content: ""; @@ -269,15 +269,15 @@ transition: .4s; border-radius: 50%; } - + input:checked + .slider { background-color: var(--primary-color); } - + input:checked + .slider:before { transform: translateX(26px); } - + .cta-button { background: var(--gradient-primary); color: white; @@ -288,13 +288,13 @@ transition: all 0.3s ease; box-shadow: var(--shadow-sm); } - + .cta-button:hover { transform: translateY(-1px); box-shadow: var(--shadow-md); color: white; } - + /* Hero Section */ .hero { padding: 220px 0 120px; @@ -303,7 +303,7 @@ position: relative; overflow: hidden; } - + .hero::before { content: ''; position: absolute; @@ -314,7 +314,7 @@ background: url('data:image/svg+xml,') repeat; opacity: 0.6; } - + .hero::after { content: ''; position: absolute; @@ -324,14 +324,14 @@ bottom: 0; background: radial-gradient(circle at 50% 50%, rgba(255,255,255,0.1) 0%, transparent 70%); } - + .hero-content { max-width: 900px; margin: 0 auto; position: relative; z-index: 1; } - + .hero h1 { font-size: 3.5rem; font-weight: 800; @@ -344,56 +344,56 @@ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); animation: fadeInUp 0.8s ease-out; } - + [data-theme="dark"] .hero h1 { color: white; } - + [data-theme="dark"] .hero p { color: rgba(255, 255, 255, 0.9); } - + [data-theme="dark"] .github-stars-display { color: rgba(255, 255, 255, 0.8) !important; } - + [data-theme="dark"] .testimonials .section-header h2 { color: white !important; } - + [data-theme="dark"] .testimonials .section-header p { color: rgba(255, 255, 255, 0.9) !important; } - + .testimonial-quote { color: #2d3748; } - + .testimonial-source { color: #4a5568; } - + [data-theme="dark"] .testimonial-quote { color: white !important; } - + [data-theme="dark"] .testimonial-source { color: rgba(255, 255, 255, 0.8) !important; } - + .testimonial-card { background: white; border: 1px solid rgba(0, 0, 0, 0.1); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } - + [data-theme="dark"] .testimonial-card { background: rgba(255, 255, 255, 0.15) !important; backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.2) !important; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3) !important; } - + .hero-subtitle { font-size: 1.5rem; font-weight: 400; @@ -403,11 +403,11 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); animation: fadeInUp 0.8s ease-out 0.1s both; } - + .hero-spacer { height: 40px; } - + .hero p { font-size: 1.3rem; color: #4a5568; @@ -420,7 +420,7 @@ z-index: 1; animation: fadeInUp 0.8s ease-out 0.2s both; } - + .hero-buttons { display: flex; justify-content: center; @@ -432,7 +432,7 @@ z-index: 1; animation: fadeInUp 0.8s ease-out 0.4s both; } - + .hero-buttons a { text-decoration: none; padding: 16px 32px; @@ -444,33 +444,33 @@ align-items: center; gap: 8px; } - + .primary-button { background-color: white; color: var(--primary-color); box-shadow: var(--shadow-lg); } - + .primary-button:hover { transform: translateY(-2px); box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); } - + .secondary-button { border: 2px solid var(--primary-color); color: var(--primary-color); } - + .secondary-button:hover { background-color: var(--primary-color); color: white; } - + [data-theme="dark"] .secondary-button { border: 2px solid white; color: white; } - + [data-theme="dark"] .secondary-button:hover { background-color: white; color: var(--primary-color); @@ -496,13 +496,13 @@ height: 100%; border-radius: 5px; } - + .hero-image-container { margin-bottom: 40px; position: relative; z-index: 1; } - + .mobile-overlay { position: absolute; bottom: -20px; @@ -516,23 +516,23 @@ pointer-events: none; animation: fadeInUp 0.8s ease-out 0.8s both; } - + .mobile-overlay-light { display: block; } - + .mobile-overlay-dark { display: none; } - + [data-theme="dark"] .mobile-overlay-light { display: none; } - + [data-theme="dark"] .mobile-overlay-dark { display: block; } - + @media (max-width: 768px) { .mobile-overlay { width: 120px; @@ -540,7 +540,7 @@ right: -10px; } } - + .hero-image { max-width: 100%; width: 100%; @@ -550,33 +550,33 @@ transition: all 0.3s ease; animation: fadeInUp 0.8s ease-out 0.6s both; } - + .hero-image-light { display: block; } - + .hero-image-dark { display: none; } - + [data-theme="dark"] .hero-image-light { display: none; } - + [data-theme="dark"] .hero-image-dark { display: block; } - + /* Features */ .features { padding: 120px 0; background-color: var(--light-color); } - + [data-theme="dark"] .features { background-color: var(--bg-color); } - + .section-header { text-align: center; margin-bottom: 80px; @@ -584,7 +584,7 @@ margin-left: auto; margin-right: auto; } - + .section-header h2 { font-size: 2.4rem; font-weight: 800; @@ -593,7 +593,7 @@ line-height: 1.2; letter-spacing: -0.02em; } - + .section-header p { font-size: 1.25rem; color: var(--text-muted); @@ -601,13 +601,13 @@ max-width: 700px; margin: 0 auto; } - + .features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; } - + .feature-card { background-color: var(--card-bg); padding: 24px 20px; @@ -619,7 +619,7 @@ position: relative; overflow: hidden; } - + .feature-card::before { content: ''; position: absolute; @@ -630,7 +630,7 @@ background: var(--gradient-primary); transition: all 0.3s ease; } - + .feature-card::after { content: ''; position: absolute; @@ -641,20 +641,20 @@ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent); transition: left 0.5s ease; } - + .feature-card:hover { transform: translateY(-8px) scale(1.02); box-shadow: var(--shadow-lg); } - + .feature-card:hover::after { left: 100%; } - + .feature-card:hover::before { height: 6px; } - + .feature-icon { background: var(--gradient-primary); color: white; @@ -668,7 +668,7 @@ font-size: 1.2rem; box-shadow: var(--shadow-md); } - + .feature-card h3 { font-size: 1.2rem; font-weight: 700; @@ -676,13 +676,13 @@ color: var(--text-color); line-height: 1.3; } - + .feature-card p { color: var(--text-muted); line-height: 1.6; font-size: 0.9rem; } - + /* Languages Section */ .languages { padding: 100px 0; @@ -691,7 +691,7 @@ position: relative; overflow: hidden; } - + .languages::before { content: ''; position: absolute; @@ -702,27 +702,27 @@ background: url('data:image/svg+xml,'); opacity: 0.3; } - + .languages .container { position: relative; z-index: 1; } - + .languages .section-header { margin-bottom: 60px; } - + .languages .section-header h2 { color: white; font-size: 2.8rem; margin-bottom: 20px; } - + .languages .section-header p { color: rgba(255, 255, 255, 0.9); font-size: 1.2rem; } - + .languages-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); @@ -730,7 +730,7 @@ max-width: 1000px; margin: 0 auto; } - + .language-item { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); @@ -741,21 +741,21 @@ transition: all 0.3s ease; cursor: pointer; } - + .language-item:hover { transform: translateY(-5px) scale(1.02); background: rgba(255, 255, 255, 0.15); border-color: rgba(255, 255, 255, 0.4); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); } - + .language-flag { font-size: 2.5rem; margin-bottom: 10px; display: block; filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3)); } - + .language-name { font-weight: 600; font-size: 0.9rem; @@ -763,44 +763,44 @@ margin: 0; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); } - - + + /* Screenshots */ .screenshots { padding: 100px 0; background-color: var(--card-bg); } - + .screenshots-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 30px; margin-top: 50px; } - + .screenshot { border-radius: 6px; overflow: hidden; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); } - + .screenshot img { width: 100%; height: auto; display: block; transition: transform 0.3s; } - + .screenshot:hover img { transform: scale(1.03); } - + /* Installation */ .installation { padding: 100px 0; background-color: var(--bg-color); } - + .installation-steps { max-width: 800px; margin: 0 auto; @@ -809,17 +809,17 @@ padding: 40px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); } - + .step { margin-bottom: 30px; display: flex; align-items: flex-start; } - + .step:last-child { margin-bottom: 0; } - + .step-number { background-color: var(--primary-color); color: white; @@ -833,18 +833,18 @@ margin-right: 15px; flex-shrink: 0; } - + .step-content h3 { font-size: 1.3rem; margin-bottom: 10px; color: var(--text-color); } - + .step-content p { color: var(--secondary-color); margin-bottom: 15px; } - + code { background-color: #f0f0f0; padding: 10px 15px; @@ -855,12 +855,12 @@ white-space: pre-wrap; color: #333; } - + [data-theme="dark"] code { background-color: #2a2a2a; color: #f0f0f0; } - + /* CTA */ .cta { padding: 100px 0; @@ -868,24 +868,24 @@ color: white; text-align: center; } - + .cta h2 { font-size: 2.5rem; margin-bottom: 20px; } - + .cta p { font-size: 1.2rem; max-width: 700px; margin: 0 auto 30px; } - + .cta-buttons { display: flex; justify-content: center; gap: 20px; } - + .cta-white { background-color: white; color: var(--primary-color); @@ -895,11 +895,11 @@ font-weight: 600; transition: background-color 0.3s; } - + .cta-white:hover { background-color: #f0f0f0; } - + .cta-outline { border: 2px solid white; color: white; @@ -909,32 +909,32 @@ font-weight: 600; transition: background-color 0.3s; } - + .cta-outline:hover { background-color: rgba(255, 255, 255, 0.1); } - + /* Footer */ footer { background-color: #212121; color: white; padding: 60px 0 30px; } - + .footer-content { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 40px; margin-bottom: 40px; } - + .footer-column h3 { font-size: 1.3rem; margin-bottom: 20px; position: relative; padding-bottom: 10px; } - + .footer-column h3::after { content: ''; position: absolute; @@ -944,25 +944,25 @@ height: 2px; background-color: var(--primary-color); } - + .footer-links { list-style: none; } - + .footer-links li { margin-bottom: 10px; } - + .footer-links a { color: #b0b0b0; text-decoration: none; transition: color 0.3s; } - + .footer-links a:hover { color: white; } - + .footer-bottom { text-align: center; padding-top: 30px; @@ -970,47 +970,47 @@ color: #b0b0b0; font-size: 0.9rem; } - + .social-icons { display: flex; gap: 15px; margin-top: 20px; } - + .social-icons a { color: white; font-size: 1.2rem; transition: color 0.3s; } - + .social-icons a:hover { color: var(--primary-color); } - + .mobile-menu-toggle { display: none; } - + /* Responsive */ @media (max-width: 768px) { .container { padding: 0 20px; } - + /* Header */ nav { padding: 15px 0; } - + .logo { font-size: 1.5rem; } - + .logo-icon svg { width: 24px; height: 24px; } - + .nav-links { display: none; position: fixed; @@ -1025,11 +1025,11 @@ box-shadow: var(--shadow-lg); z-index: 1000; } - + .nav-links.active { display: flex; } - + .mobile-menu-toggle { display: block; background: none; @@ -1039,49 +1039,49 @@ cursor: pointer; padding: 5px; } - + .nav-links a { padding: 10px 15px; border-radius: 8px; text-align: center; } - + .theme-switch { justify-content: center; margin: 10px 0; } - + /* Hero */ .hero { padding: 150px 0 60px; text-align: center; } - + .hero h1 { font-size: 2.2rem; margin-bottom: 12px; } - + .hero-subtitle { font-size: 1.1rem; margin-bottom: 20px; } - + .hero-spacer { height: 25px; } - + .hero p { font-size: 1rem; margin-bottom: 30px; } - + .hero-buttons { flex-direction: column; gap: 12px; align-items: center; } - + .hero-buttons a { width: 100%; max-width: 280px; @@ -1089,28 +1089,28 @@ padding: 12px 20px; font-size: 0.95rem; } - + .hero-image { max-width: 100%; margin-bottom: 30px; } - + /* Sections */ .section-header h2 { font-size: 2rem; margin-bottom: 12px; } - + .section-header p { font-size: 0.95rem; } - + /* Features */ .features-grid { grid-template-columns: 1fr; gap: 20px; } - + .feature-card { padding: 20px; text-align: left; @@ -1120,7 +1120,7 @@ gap: 15px 15px; align-items: start; } - + .feature-icon { grid-row: 1 / 3; grid-column: 1; @@ -1128,7 +1128,7 @@ align-self: start; margin-top: 3px; } - + .feature-card h3 { grid-row: 1; grid-column: 2; @@ -1136,7 +1136,7 @@ margin: 0; align-self: start; } - + .feature-card p { grid-row: 2; grid-column: 2; @@ -1145,12 +1145,12 @@ line-height: 1.4; align-self: start; } - + /* Testimonials */ .testimonials { padding: 50px 0; } - + .testimonial-card { padding: 25px 20px !important; flex-direction: column; @@ -1160,7 +1160,7 @@ background: rgba(255, 255, 255, 0.95) !important; backdrop-filter: blur(10px) !important; } - + .testimonial-card blockquote { font-size: 1.1rem !important; order: 2; @@ -1169,116 +1169,116 @@ line-height: 1.5; margin: 0 !important; } - + .testimonial-card > div:first-child { font-size: 2.5rem !important; order: 1; } - + .testimonial-card > div:last-child { order: 3; justify-content: center; color: #4a5568 !important; font-weight: 600; } - + /* Languages */ .languages-grid { grid-template-columns: repeat(3, 1fr); gap: 15px; } - + .language-item { padding: 10px; } - + .language-flag { font-size: 1.5rem; } - + .language-name { font-size: 0.8rem; } - + /* Screenshots */ .screenshots-grid { grid-template-columns: 1fr; gap: 20px; } - + /* Installation */ .installation-steps { gap: 30px; } - + .step { flex-direction: column; text-align: center; gap: 15px; } - + .step-number { margin: 0 auto 15px; } - + .step-content h3 { font-size: 1.1rem; word-wrap: break-word; } - + .step-content p { font-size: 0.9rem; } - + .step-content code { font-size: 0.8rem; word-break: break-all; white-space: pre-wrap; overflow-wrap: break-word; } - + .step-content div { margin: 10px 0; overflow: hidden; } - + .step-content h4 { font-size: 1rem !important; word-wrap: break-word; } - + .step-content ul { text-align: left; padding-left: 15px !important; } - + .step-content li { font-size: 0.85rem; margin-bottom: 8px; word-wrap: break-word; } - + .step-content strong { word-wrap: break-word; display: inline-block; max-width: 100%; } - + /* CTA */ .cta h2 { font-size: 2rem; } - + .cta p { font-size: 0.95rem; } - + .cta-buttons { flex-direction: column; gap: 12px; align-items: center; } - + .cta-buttons a { width: 100%; max-width: 280px; @@ -1357,14 +1357,14 @@ 20% { opacity: 0; transform: translateY(-20px); } 100% { opacity: 0; transform: translateY(-20px); } } - + .testimonial-card { animation: testimonialFade 20s infinite; position: absolute; width: 100%; opacity: 0; } - + .testimonial-card:nth-child(1) { animation-delay: 0s; } .testimonial-card:nth-child(2) { animation-delay: 4s; } .testimonial-card:nth-child(3) { animation-delay: 8s; } @@ -1697,7 +1697,6 @@

Optional Variables:

@@ -1713,7 +1712,6 @@ -e TUDUDI_USER_EMAIL=myemail@example.com \ -e TUDUDI_USER_PASSWORD=mysecurepassword \ -e TUDUDI_SESSION_SECRET=$(openssl rand -hex 64) \ - -e TUDUDI_INTERNAL_SSL_ENABLED=false \ -e TUDUDI_ALLOWED_ORIGINS=https://tududi,http://tududi:3002 \ -v ~/tududi_db:/app/backend/db \ -v ~/tududi_uploads:/app/backend/uploads \ @@ -1845,15 +1843,15 @@