diff --git a/frontEnd/app/services/[slug]/page.tsx b/frontEnd/app/services/[slug]/page.tsx
index 5a4ca70c..9c096843 100644
--- a/frontEnd/app/services/[slug]/page.tsx
+++ b/frontEnd/app/services/[slug]/page.tsx
@@ -12,7 +12,11 @@ import ServicesInitAnimations from "@/components/pages/services/ServicesInitAnim
import { Service } from "@/lib/api/serviceService";
import { generateServiceMetadata } from "@/lib/seo/metadata";
import { ServiceSchema, BreadcrumbSchema } from "@/components/shared/seo/StructuredData";
+<<<<<<< HEAD
import { API_CONFIG, getApiHeaders } from "@/lib/config/api";
+=======
+import { API_CONFIG } from "@/lib/config/api";
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
interface ServicePageProps {
params: Promise<{
@@ -24,6 +28,7 @@ interface ServicePageProps {
// This pre-generates known pages, but new pages can still be generated on-demand
export async function generateStaticParams() {
try {
+<<<<<<< HEAD
// Use internal API URL for server-side requests
const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:1086';
const response = await fetch(
@@ -31,6 +36,15 @@ export async function generateStaticParams() {
{
method: 'GET',
headers: getApiHeaders(),
+=======
+ const response = await fetch(
+ `${API_CONFIG.BASE_URL}/api/services/`,
+ {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
next: { revalidate: 60 }, // Revalidate every minute for faster image updates
}
);
@@ -57,6 +71,7 @@ export async function generateMetadata({ params }: ServicePageProps) {
const { slug } = await params;
try {
+<<<<<<< HEAD
// Use internal API URL for server-side requests
const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:1086';
const response = await fetch(
@@ -64,6 +79,15 @@ export async function generateMetadata({ params }: ServicePageProps) {
{
method: 'GET',
headers: getApiHeaders(),
+=======
+ const response = await fetch(
+ `${API_CONFIG.BASE_URL}/api/services/${slug}/`,
+ {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
next: { revalidate: 60 }, // Revalidate every minute for faster image updates
}
);
@@ -87,6 +111,7 @@ const ServicePage = async ({ params }: ServicePageProps) => {
const { slug } = await params;
try {
+<<<<<<< HEAD
// Use internal API URL for server-side requests
const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:1086';
const response = await fetch(
@@ -94,6 +119,15 @@ const ServicePage = async ({ params }: ServicePageProps) => {
{
method: 'GET',
headers: getApiHeaders(),
+=======
+ const response = await fetch(
+ `${API_CONFIG.BASE_URL}/api/services/${slug}/`,
+ {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
next: { revalidate: 60 }, // Revalidate every minute for faster image updates
}
);
diff --git a/frontEnd/app/support-center/page.tsx b/frontEnd/app/support-center/page.tsx
index 0d3ac26c..bf7922b4 100644
--- a/frontEnd/app/support-center/page.tsx
+++ b/frontEnd/app/support-center/page.tsx
@@ -12,8 +12,29 @@ type ModalType = 'create' | 'knowledge' | 'status' | null;
const SupportCenterPage = () => {
// Set metadata for client component
useEffect(() => {
+<<<<<<< HEAD
// Only run on client side
if (typeof window === 'undefined' || typeof document === 'undefined') return;
+=======
+ const metadata = createMetadata({
+ title: "Support Center - Enterprise Support & Help Desk",
+ description: "Get 24/7 enterprise support from GNX Soft. Access our knowledge base, create support tickets, check ticket status, and get help with our software solutions and services.",
+ keywords: [
+ "Support Center",
+ "Customer Support",
+ "Help Desk",
+ "Technical Support",
+ "Knowledge Base",
+ "Support Tickets",
+ "Enterprise Support",
+ "IT Support",
+ ],
+ url: "/support-center",
+ });
+
+ const titleString = typeof metadata.title === 'string' ? metadata.title : "Support Center | GNX Soft";
+ document.title = titleString;
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
try {
const metadata = createMetadata({
diff --git a/frontEnd/components/pages/about/AboutBanner.tsx b/frontEnd/components/pages/about/AboutBanner.tsx
index 8bb89e81..91fba988 100644
--- a/frontEnd/components/pages/about/AboutBanner.tsx
+++ b/frontEnd/components/pages/about/AboutBanner.tsx
@@ -227,7 +227,11 @@ const AboutBanner = () => {
{/* Social Links */}
+<<<<<<< HEAD
+=======
+
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
diff --git a/frontEnd/components/pages/career/CareerBanner.tsx b/frontEnd/components/pages/career/CareerBanner.tsx
index feb99389..94a11165 100644
--- a/frontEnd/components/pages/career/CareerBanner.tsx
+++ b/frontEnd/components/pages/career/CareerBanner.tsx
@@ -117,7 +117,11 @@ const CareerBanner = () => {
-
>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
target="_blank"
aria-label="connect with us on linkedin"
>
diff --git a/frontEnd/components/pages/case-study/CaseSingle.tsx b/frontEnd/components/pages/case-study/CaseSingle.tsx
index 94b1ca60..dcccac07 100644
--- a/frontEnd/components/pages/case-study/CaseSingle.tsx
+++ b/frontEnd/components/pages/case-study/CaseSingle.tsx
@@ -5,7 +5,10 @@ import Link from "next/link";
import { useRouter } from "next/navigation";
import { useCaseStudy } from "@/lib/hooks/useCaseStudy";
import { getImageUrl } from "@/lib/imageUtils";
+<<<<<<< HEAD
import { sanitizeHTML } from "@/lib/security/sanitize";
+=======
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
interface CaseSingleProps {
slug: string;
diff --git a/frontEnd/components/pages/services/ServicesBanner.tsx b/frontEnd/components/pages/services/ServicesBanner.tsx
index 1dac7989..14e9efe0 100644
--- a/frontEnd/components/pages/services/ServicesBanner.tsx
+++ b/frontEnd/components/pages/services/ServicesBanner.tsx
@@ -84,7 +84,11 @@ const ServicesBanner = () => {
-
>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
target="_blank"
aria-label="connect with us on linkedin"
>
diff --git a/frontEnd/components/shared/layout/footer/Footer.tsx b/frontEnd/components/shared/layout/footer/Footer.tsx
index e50d0a80..b51f450b 100644
--- a/frontEnd/components/shared/layout/footer/Footer.tsx
+++ b/frontEnd/components/shared/layout/footer/Footer.tsx
@@ -289,7 +289,11 @@ const Footer = () => {
>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
target="_blank"
rel="noopener noreferrer"
title="LinkedIn"
@@ -298,7 +302,11 @@ const Footer = () => {
>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
target="_blank"
rel="noopener noreferrer"
title="GitHub"
diff --git a/frontEnd/components/shared/layout/header/OffcanvasMenu.tsx b/frontEnd/components/shared/layout/header/OffcanvasMenu.tsx
index 04b15d4b..1926c0fb 100644
--- a/frontEnd/components/shared/layout/header/OffcanvasMenu.tsx
+++ b/frontEnd/components/shared/layout/header/OffcanvasMenu.tsx
@@ -175,7 +175,11 @@ const OffcanvasMenu = ({
-
>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
target="_blank"
aria-label="Connect with us on LinkedIn"
>
diff --git a/frontEnd/next.config.js b/frontEnd/next.config.js
index 175695ce..ec53db36 100644
--- a/frontEnd/next.config.js
+++ b/frontEnd/next.config.js
@@ -123,9 +123,13 @@ const nextConfig = {
// Note: Removed conflicting directives that are ignored with 'strict-dynamic'
{
key: 'Content-Security-Policy',
+<<<<<<< HEAD
value: process.env.NODE_ENV === 'production'
? "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' https://www.google-analytics.com; frame-src 'self' https://www.google.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; object-src 'none'; upgrade-insecure-requests;"
: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https: http://localhost:8000 http://localhost:8080; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' http://localhost:8000 https://www.google-analytics.com; frame-src 'self' https://www.google.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self';"
+=======
+ value: "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https: http://localhost:8000 http://localhost:8080; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' http://localhost:8000 https://www.google-analytics.com; frame-src 'self' https://www.google.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self'"
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
},
// Hide X-Powered-By header from Next.js
{
diff --git a/nginx-gnxsoft.conf b/nginx-gnxsoft.conf
index 11aaa4e0..012e0a29 100644
--- a/nginx-gnxsoft.conf
+++ b/nginx-gnxsoft.conf
@@ -11,6 +11,14 @@
# 3. Database: PostgreSQL on host (port 5433 to avoid conflict with Docker instance on 5432)
# 4. Use install-postgresql.sh to install and configure PostgreSQL
# 5. Use start-services.sh to start both backend and frontend services
+<<<<<<< HEAD
+=======
+#
+# NOTE: Rate limiting zones must be defined in the main nginx.conf http context
+# Add these lines to /etc/nginx/nginx.conf inside the http {} block:
+# limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
+# limit_req_zone $binary_remote_addr zone=general_limit:10m rate=100r/s;
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
# Frontend - Public facing (Next.js Production Server on port 1087)
upstream frontend {
@@ -64,9 +72,7 @@ server {
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()" always;
- # Rate Limiting Zones
- limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
- limit_req_zone $binary_remote_addr zone=general_limit:10m rate=100r/s;
+ # Rate Limiting (zones must be defined in main nginx.conf http context)
limit_req_status 429;
# Client settings
@@ -78,7 +84,147 @@ server {
access_log /var/log/nginx/gnxsoft_access.log;
error_log /var/log/nginx/gnxsoft_error.log warn;
- # Root location - Frontend (Next.js)
+ # IMPORTANT: More specific location blocks MUST come before location /
+ # Order matters in nginx - longest match wins
+
+ # API Proxy - Frontend talks to backend ONLY through this internal proxy
+ # Backend port 1086 is BLOCKED from internet by firewall
+ location /api/ {
+ limit_req zone=api_limit burst=20 nodelay;
+
+ # Internal proxy to backend (127.0.0.1:1086)
+ # Backend is NOT accessible from public internet
+ proxy_pass http://backend_internal/api/;
+ proxy_http_version 1.1;
+
+ # Add internal API key (must match INTERNAL_API_KEY in Django .env)
+ set $api_key "9hZtPwyScigoBAl59Uvcz_9VztSRC6Zt_6L1B2xTM2M";
+ proxy_set_header X-Internal-API-Key $api_key;
+
+ # Backend sees request as coming from localhost
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header X-Forwarded-Host $host;
+ proxy_set_header X-Forwarded-Port $server_port;
+
+ # Hide backend server info
+ proxy_hide_header X-Powered-By;
+ proxy_hide_header Server;
+
+ # Timeouts
+ proxy_connect_timeout 30s;
+ proxy_send_timeout 30s;
+ proxy_read_timeout 30s;
+
+ # CORS headers (if needed)
+ add_header Access-Control-Allow-Origin "https://gnxsoft.com" always;
+ add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
+ add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Internal-API-Key" always;
+ add_header Access-Control-Allow-Credentials "true" always;
+
+ # Handle preflight requests
+ if ($request_method = 'OPTIONS') {
+ return 204;
+ }
+ }
+
+ # Media files (served by nginx directly for better performance)
+ location /media/ {
+ alias /var/www/GNX-WEB/backEnd/media/;
+ expires 30d;
+ add_header Cache-Control "public, immutable";
+ access_log off;
+
+ # Security
+ location ~ \.(php|py|pl|sh)$ {
+ deny all;
+ }
+ }
+
+ # Static files (served by nginx directly)
+ location /static/ {
+ alias /var/www/GNX-WEB/backEnd/staticfiles/;
+ expires 1y;
+ add_header Cache-Control "public, immutable";
+ access_log off;
+ }
+
+ # Next.js image optimization API - must be proxied to Next.js server
+ # Use regex to match /_next/image with query strings
+ location ~ ^/_next/image {
+ proxy_pass http://frontend;
+ proxy_http_version 1.1;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header X-Forwarded-Host $host;
+ proxy_set_header X-Forwarded-Port $server_port;
+
+ # Preserve query string
+ proxy_pass_request_headers on;
+
+ # Timeouts for image processing
+ proxy_connect_timeout 30s;
+ proxy_send_timeout 30s;
+ proxy_read_timeout 30s;
+
+ # Buffer settings for image processing
+ proxy_buffering on;
+ proxy_buffer_size 4k;
+ proxy_buffers 8 4k;
+
+ # Cache optimized images
+ proxy_cache_valid 200 1d;
+ add_header Cache-Control "public, max-age=86400";
+ }
+
+ # Next.js static files - serve directly from filesystem for better performance
+ location /_next/static/ {
+ alias /var/www/GNX-WEB/frontEnd/.next/static/;
+ expires 1y;
+ add_header Cache-Control "public, immutable";
+ access_log off;
+
+ # Correct MIME types
+ types {
+ text/css css;
+ application/javascript js;
+ application/json json;
+ font/woff2 woff2;
+ font/woff woff;
+ font/ttf ttf;
+ image/png png;
+ image/jpeg jpg jpeg;
+ image/webp webp;
+ image/svg+xml svg;
+ }
+ }
+
+ # Frontend public images - use prefix location (must come before root location)
+ location /images/ {
+ alias /var/www/GNX-WEB/frontEnd/public/images/;
+ expires 30d;
+ add_header Cache-Control "public, immutable";
+ access_log off;
+
+ # Ensure proper MIME types
+ types {
+ image/png png;
+ image/jpeg jpg jpeg;
+ image/gif gif;
+ image/svg+xml svg;
+ image/webp webp;
+ }
+ default_type application/octet-stream;
+
+ # Try files, then fallback
+ try_files $uri =404;
+ }
+
+ # Root location - Frontend (Next.js) - MUST be last
location / {
limit_req zone=general_limit burst=50 nodelay;
@@ -98,6 +244,7 @@ server {
proxy_read_timeout 60s;
}
+<<<<<<< HEAD
# API Proxy - Frontend talks to backend ONLY through this internal proxy
# Backend port 1086 is BLOCKED from internet by firewall
location /api/ {
@@ -173,6 +320,8 @@ server {
access_log off;
}
+=======
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
# Deny access to hidden files
location ~ /\. {
deny all;
@@ -184,7 +333,11 @@ server {
location /admin/ {
# IP restriction is handled by Django middleware
# Add internal API key (must match INTERNAL_API_KEY in Django .env)
+<<<<<<< HEAD
set $api_key "PLACEHOLDER_INTERNAL_API_KEY";
+=======
+ set $api_key "9hZtPwyScigoBAl59Uvcz_9VztSRC6Zt_6L1B2xTM2M";
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
proxy_set_header X-Internal-API-Key $api_key;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
@@ -220,7 +373,10 @@ server {
# 5. Public internet can ONLY access nginx (ports 80, 443)
# 6. All API calls go through nginx proxy (/api/* → 127.0.0.1:1086/api/*)
# 7. Backend IP whitelist middleware ensures only localhost requests
+<<<<<<< HEAD
# 8. Update PLACEHOLDER_INTERNAL_API_KEY with actual key from Django .env
+=======
+# 8. Rate limiting zones must be added to /etc/nginx/nginx.conf http {} block
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
# 9. PostgreSQL runs on port 5433 (to avoid conflict with Docker on 5432)
# ==============================================================================
-
diff --git a/nginx-rate-limit-zones.conf b/nginx-rate-limit-zones.conf
new file mode 100644
index 00000000..94861fdf
--- /dev/null
+++ b/nginx-rate-limit-zones.conf
@@ -0,0 +1,7 @@
+# Rate Limiting Zones for GNX-WEB
+# Add these lines to /etc/nginx/nginx.conf inside the http {} block
+# Or include this file in /etc/nginx/nginx.conf with: include /etc/nginx/conf.d/rate-limit-zones.conf;
+
+limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
+limit_req_zone $binary_remote_addr zone=general_limit:10m rate=100r/s;
+
diff --git a/start-services.sh b/start-services.sh
index 5eab0dc7..6aad3dfb 100755
--- a/start-services.sh
+++ b/start-services.sh
@@ -211,10 +211,48 @@ else
fi
fi
+<<<<<<< HEAD
# Set PORT environment variable and start with PM2
PORT=$FRONTEND_PORT NODE_ENV=production pm2 start npm \
--name "gnxsoft-frontend" \
-- start
+=======
+# Check if Next.js is using standalone output mode
+if grep -q '"output":\s*"standalone"' next.config.js 2>/dev/null || grep -q "output:.*'standalone'" next.config.js 2>/dev/null; then
+ echo -e "${BLUE}Detected standalone mode. Starting with standalone server...${NC}"
+
+ # Check if standalone server exists
+ if [ ! -f ".next/standalone/server.js" ]; then
+ echo -e "${YELLOW}Standalone server not found. Rebuilding...${NC}"
+ NODE_ENV=production PORT=$FRONTEND_PORT npm run build
+ fi
+
+ # Ensure public folder is copied to standalone directory
+ if [ ! -d ".next/standalone/public" ]; then
+ echo -e "${BLUE}Copying public folder to standalone directory...${NC}"
+ cp -r public .next/standalone/
+ fi
+
+ # Ensure static folder symlink exists for image optimization
+ if [ ! -L ".next/standalone/.next/static" ] && [ ! -d ".next/standalone/.next/static" ]; then
+ echo -e "${BLUE}Creating symlink for static files...${NC}"
+ mkdir -p .next/standalone/.next
+ ln -s ../../static .next/standalone/.next/static
+ fi
+
+ # Start standalone server with PM2
+ PORT=$FRONTEND_PORT NODE_ENV=production pm2 start node \
+ --name "gnxsoft-frontend" \
+ --cwd "$FRONTEND_DIR" \
+ -- \
+ ".next/standalone/server.js"
+else
+ # Standard Next.js start
+ PORT=$FRONTEND_PORT NODE_ENV=production pm2 start npm \
+ --name "gnxsoft-frontend" \
+ -- start
+fi
+>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
# Save PM2 configuration
pm2 save
diff --git a/switch-to-sqlite.sh b/switch-to-sqlite.sh
new file mode 100644
index 00000000..7da778da
--- /dev/null
+++ b/switch-to-sqlite.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# Switch Django backend to use SQLite instead of PostgreSQL
+
+BACKEND_DIR="/var/www/GNX-WEB/backEnd"
+BACKEND_ENV="$BACKEND_DIR/.env"
+
+echo "Switching to SQLite database..."
+
+if [ -f "$BACKEND_ENV" ]; then
+ # Comment out or remove DATABASE_URL line
+ if grep -q "^DATABASE_URL=" "$BACKEND_ENV"; then
+ echo "Commenting out DATABASE_URL to use SQLite..."
+ sed -i 's|^DATABASE_URL=.*|# DATABASE_URL= # Using SQLite instead|' "$BACKEND_ENV"
+ echo "✓ DATABASE_URL commented out"
+ fi
+
+ # Ensure db.sqlite3 path is correct (it should be in backEnd directory)
+ echo ""
+ echo "SQLite database will be at: $BACKEND_DIR/db.sqlite3"
+ echo ""
+ echo "Restarting backend to apply changes..."
+ pm2 restart gnxsoft-backend
+ echo "✓ Backend restarted"
+ echo ""
+ echo "Checking database connection..."
+ cd "$BACKEND_DIR"
+ source venv/bin/activate
+ python manage.py check --database default
+else
+ echo "Error: .env file not found at $BACKEND_ENV"
+ exit 1
+fi
+