Skip to content

Commit 7a43f47

Browse files
committed
add FTP account configuration for deployment
1 parent a7fee23 commit 7a43f47

File tree

12 files changed

+1259
-853
lines changed

12 files changed

+1259
-853
lines changed

.htaccess

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
# Node.js App Configuration
12
RewriteEngine On
2-
RewriteBase /
3-
RewriteRule ^index\.html$ - [L]
3+
4+
# Handle Next.js routing
45
RewriteCond %{REQUEST_FILENAME} !-f
56
RewriteCond %{REQUEST_FILENAME} !-d
6-
RewriteRule . /index.html [L]
7+
RewriteRule ^(.*)$ /index.html [QSA,L]
8+
9+
# Security headers
10+
Header always set X-Frame-Options DENY
11+
Header always set X-Content-Type-Options nosniff
12+
Header always set X-XSS-Protection "1; mode=block"

deploy.sh

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/bin/bash
2+
3+
# Automated Node.js Deployment Script for cPanel
4+
# This script automates the deployment process as much as possible
5+
6+
echo "🚀 Starting automated Node.js deployment..."
7+
8+
# Configuration
9+
APP_DIR="/home/csulb/public_html"
10+
NODE_APP_NAME="acm-website"
11+
12+
# Function to check if Node.js app exists
13+
check_node_app() {
14+
echo "📋 Checking if Node.js app exists..."
15+
# This would check cPanel API - placeholder for now
16+
return 0
17+
}
18+
19+
# Function to install dependencies
20+
install_dependencies() {
21+
echo "📦 Installing dependencies..."
22+
cd $APP_DIR
23+
npm install --production
24+
if [ $? -eq 0 ]; then
25+
echo "✅ Dependencies installed successfully"
26+
else
27+
echo "❌ Failed to install dependencies"
28+
exit 1
29+
fi
30+
}
31+
32+
# Function to build the application
33+
build_app() {
34+
echo "🔨 Building application..."
35+
cd $APP_DIR
36+
npm run build
37+
if [ $? -eq 0 ]; then
38+
echo "✅ Application built successfully"
39+
else
40+
echo "❌ Failed to build application"
41+
exit 1
42+
fi
43+
}
44+
45+
# Function to restart the Node.js app
46+
restart_app() {
47+
echo "🔄 Restarting Node.js application..."
48+
# This would use cPanel API to restart the app
49+
echo "⚠️ Manual step required: Please restart your Node.js app in cPanel"
50+
echo " 1. Go to cPanel → Node.js Apps"
51+
echo " 2. Find your app and click 'Restart'"
52+
}
53+
54+
# Main deployment process
55+
main() {
56+
echo "🎯 Starting deployment process..."
57+
58+
# Check if app directory exists
59+
if [ ! -d "$APP_DIR" ]; then
60+
echo "❌ App directory not found: $APP_DIR"
61+
exit 1
62+
fi
63+
64+
# Install dependencies
65+
install_dependencies
66+
67+
# Build application
68+
build_app
69+
70+
# Restart app (manual step)
71+
restart_app
72+
73+
echo "🎉 Deployment process completed!"
74+
echo "📝 Next steps:"
75+
echo " 1. Restart your Node.js app in cPanel"
76+
echo " 2. Check your application logs"
77+
echo " 3. Test your website"
78+
}
79+
80+
# Run main function
81+
main

deploy.yml

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
name: Build & Deploy Next.js to cPanel via FTP
1+
name: Build & Deploy Next.js to cPanel (Dynamic)
22

33
on:
44
push:
5-
branches: [ main ]
5+
branches: [main]
66
workflow_dispatch:
77

88
jobs:
@@ -14,26 +14,44 @@ jobs:
1414
- uses: actions/setup-node@v4
1515
with:
1616
node-version: 20
17-
cache: 'npm'
17+
cache: "npm"
1818

1919
- name: Install deps
2020
run: npm ci
2121

22-
- name: Build (Next static export)
22+
- name: Build Next.js app
2323
env:
2424
NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL }}
25-
run: npm run build # produces out/
25+
MONGODB_URI: ${{ secrets.MONGODB_URI }}
26+
JWT_SECRET: ${{ secrets.JWT_SECRET }}
27+
BREVO_API_KEY: ${{ secrets.BREVO_API_KEY }}
28+
run: npm run build
2629

2730
- name: Deploy via FTP to cPanel
2831
uses: SamKirkland/FTP-Deploy-Action@v4
2932
with:
3033
server: ${{ secrets.FTP_HOST }}
3134
username: ${{ secrets.FTP_USER }}
3235
password: ${{ secrets.FTP_PASS }}
33-
local-dir: out/
34-
server-dir: public_html/
36+
local-dir: ./
37+
server-dir: public_html/acm/
3538
exclude: |
3639
**/.git*
3740
**/.github*
3841
**/node_modules/**
3942
**/*.map
43+
**/.next/cache/**
44+
**/out/**
45+
**/.env*
46+
**/README.md
47+
**/deploy.yml
48+
**/deploy.sh
49+
50+
- name: Notify deployment completion
51+
run: |
52+
echo "🚀 Deployment completed!"
53+
echo "📝 Manual steps required:"
54+
echo " 1. Go to cPanel → Node.js Apps"
55+
echo " 2. Click 'Install Dependencies'"
56+
echo " 3. Click 'Build Application'"
57+
echo " 4. Click 'Start Application'"

next.config.mjs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
/** @type {import('next').NextConfig} */
22
const nextConfig = {
3-
output: 'export', // static export
4-
images: { unoptimized: true }, // needed when exporting
5-
// If deploying to a subfolder like /app:
6-
// basePath: '/app',
7-
// assetPrefix: '/app/',
8-
// trailingSlash: true,
3+
// Dynamic deployment - no static export
4+
// output: "export", // static export
5+
// images: { unoptimized: true }, // needed when exporting
6+
7+
// Enable image optimization for better performance
8+
images: {
9+
domains: ["localhost", "csulb.acm.org"], // Add your domain here
10+
},
11+
12+
// Configure for subfolder deployment
13+
basePath: "/acm",
14+
assetPrefix: "/acm/",
15+
16+
// If deploying to root:
17+
// basePath: '/',
18+
// assetPrefix: '/',
919
};
1020
export default nextConfig;

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
"scripts": {
66
"dev": "next dev",
77
"build": "next build",
8-
"start": "next start",
9-
"lint": "next lint",
10-
"export": "next export"
8+
"start": "node server.js",
9+
"lint": "next lint"
1110
},
1211
"dependencies": {
1312
"@fortawesome/free-brands-svg-icons": "^6.6.0",

server.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const { createServer } = require("http");
2+
const { parse } = require("url");
3+
const next = require("next");
4+
5+
const dev = process.env.NODE_ENV !== "production";
6+
const hostname = "localhost";
7+
const port = process.env.PORT || 3000;
8+
9+
// when using middleware `hostname` and `port` must be provided below
10+
const app = next({ dev, hostname, port });
11+
const handle = app.getRequestHandler();
12+
13+
app.prepare().then(() => {
14+
createServer(async (req, res) => {
15+
try {
16+
// Be sure to pass `true` as the second argument to `url.parse`.
17+
// This tells it to parse the query portion of the URL.
18+
const parsedUrl = parse(req.url, true);
19+
const { pathname, query } = parsedUrl;
20+
21+
await handle(req, res, parsedUrl);
22+
} catch (err) {
23+
console.error("Error occurred handling", req.url, err);
24+
res.statusCode = 500;
25+
res.end("internal server error");
26+
}
27+
})
28+
.once("error", (err) => {
29+
console.error(err);
30+
process.exit(1);
31+
})
32+
.listen(port, () => {
33+
console.log(`> Ready on http://${hostname}:${port}`);
34+
});
35+
});

src/app/about/page.js

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"use client";
22

3-
import { useState, useEffect } from 'react';
4-
import { motion } from 'framer-motion';
5-
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
6-
import { faLinkedin, faGithub } from '@fortawesome/free-brands-svg-icons';
7-
import { faEnvelope, faGlobe } from '@fortawesome/free-solid-svg-icons';
3+
import { useState, useEffect } from "react";
4+
import { motion } from "framer-motion";
5+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
6+
import { faLinkedin, faGithub } from "@fortawesome/free-brands-svg-icons";
7+
import { faEnvelope, faGlobe } from "@fortawesome/free-solid-svg-icons";
88

99
export default function About() {
1010
const [officers, setOfficers] = useState([]); // Initialize as an empty array
@@ -15,19 +15,19 @@ export default function About() {
1515
const fetchOfficers = async () => {
1616
try {
1717
// Fetch from the correct endpoint to get all officers
18-
const response = await fetch('/api/about/officers');
19-
if (!response.ok) throw new Error('Failed to fetch officers data');
18+
const response = await fetch("/api/about/officers");
19+
if (!response.ok) throw new Error("Failed to fetch officers data");
2020

2121
const data = await response.json();
2222

2323
if (Array.isArray(data)) {
2424
setOfficers(data); // Set officers data if it's an array
2525
setIsLoaded(true); // Set loaded state to true after data is fetched
2626
} else {
27-
throw new Error('Unexpected data format');
27+
throw new Error("Unexpected data format");
2828
}
2929
} catch (error) {
30-
console.error('Error fetching officers:', error);
30+
console.error("Error fetching officers:", error);
3131
setError(error.message);
3232
}
3333
};
@@ -38,8 +38,12 @@ export default function About() {
3838
return (
3939
<div className="min-h-screen bg-white">
4040
{/* About Header with Animated Background */}
41-
<div className="relative area"> {/* Wrapper for animated background */}
42-
<ul className="circles"> {/* Floating circles */}
41+
<div className="relative area">
42+
{" "}
43+
{/* Wrapper for animated background */}
44+
<ul className="circles">
45+
{" "}
46+
{/* Floating circles */}
4347
<li></li>
4448
<li></li>
4549
<li></li>
@@ -51,7 +55,6 @@ export default function About() {
5155
<li></li>
5256
<li></li>
5357
</ul>
54-
5558
<motion.section
5659
className="relative z-10 text-white py-20 text-center"
5760
initial={{ opacity: 0, y: -50 }}
@@ -61,7 +64,10 @@ export default function About() {
6164
<div className="container mx-auto">
6265
<h1 className="text-5xl font-extrabold">About ACM at CSULB</h1>
6366
<p className="text-xl mt-4 max-w-3xl mx-auto">
64-
ACM at CSULB is dedicated to advancing computing as a science and profession. We organize regular events, workshops, and hackathons to foster learning, collaboration, and innovation among our members.
67+
ACM at CSULB is dedicated to advancing computing as a science and
68+
profession. We organize regular events, workshops, and hackathons
69+
to foster learning, collaboration, and innovation among our
70+
members.
6571
</p>
6672
</div>
6773
</motion.section>
@@ -71,12 +77,15 @@ export default function About() {
7177
<motion.section
7278
className="container mx-auto py-12 text-center text-black"
7379
initial={{ opacity: 0 }}
74-
animate={{opacity: 1}}
80+
animate={{ opacity: 1 }}
7581
transition={{ duration: 0.5 }}
7682
>
7783
<h2 className="text-3xl font-bold">Our Mission</h2>
7884
<p className="text-lg mt-4 max-w-2xl mx-auto mb-4">
79-
Our mission is to empower students with knowledge, resources, and opportunities to thrive in the field of computing. We aim to build a bridge between students and industry professionals by hosting workshops, networking events, and technical challenges.
85+
Our mission is to empower students with knowledge, resources, and
86+
opportunities to thrive in the field of computing. We aim to build a
87+
bridge between students and industry professionals by hosting
88+
workshops, networking events, and technical challenges.
8089
</p>
8190
</motion.section>
8291

@@ -91,9 +100,16 @@ export default function About() {
91100
<h2 className="text-3xl font-bold text-black">Meet Our Officers</h2>
92101
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mt-8 text-gray-900 text-xl">
93102
{officers.map((officer, index) => (
94-
<div key={index} className="our-team border-solid border-2 border-black shadow-lg">
103+
<div
104+
key={index}
105+
className="our-team border-solid border-2 border-black shadow-lg"
106+
>
95107
<div className="picture">
96-
<img className="img-fluid" src={officer.image} alt={officer.name} />
108+
<img
109+
className="img-fluid"
110+
src={officer.image}
111+
alt={officer.name}
112+
/>
97113
</div>
98114
<div className="team-content">
99115
<h3 className="name">{officer.name}</h3>
@@ -102,28 +118,43 @@ export default function About() {
102118
<ul className="social flex justify-center mt-4 space-x-4">
103119
{officer.linkedin && (
104120
<li>
105-
<a href={officer.linkedin} target="_blank" className="text-blue-600 hover:text-blue-800">
121+
<a
122+
href={officer.linkedin}
123+
target="_blank"
124+
className="text-blue-600 hover:text-blue-800"
125+
>
106126
<FontAwesomeIcon icon={faLinkedin} size="lg" />
107127
</a>
108128
</li>
109129
)}
110130
{officer.email && (
111131
<li>
112-
<a href={`mailto:${officer.email}`} className="text-blue-600 hover:text-blue-800">
132+
<a
133+
href={`mailto:${officer.email}`}
134+
className="text-blue-600 hover:text-blue-800"
135+
>
113136
<FontAwesomeIcon icon={faEnvelope} size="lg" />
114137
</a>
115138
</li>
116139
)}
117140
{officer.github && (
118141
<li>
119-
<a href={officer.github} target="_blank" className="text-blue-600 hover:text-blue-800">
142+
<a
143+
href={officer.github}
144+
target="_blank"
145+
className="text-blue-600 hover:text-blue-800"
146+
>
120147
<FontAwesomeIcon icon={faGithub} size="lg" />
121148
</a>
122149
</li>
123150
)}
124151
{officer.website && (
125152
<li>
126-
<a href={officer.website} target="_blank" className="text-blue-600 hover:text-blue-800">
153+
<a
154+
href={officer.website}
155+
target="_blank"
156+
className="text-blue-600 hover:text-blue-800"
157+
>
127158
<FontAwesomeIcon icon={faGlobe} size="lg" />
128159
</a>
129160
</li>

0 commit comments

Comments
 (0)