This commit is contained in:
Iliyan Angelov
2025-12-10 01:37:23 +02:00
13 changed files with 326 additions and 5 deletions

View File

@@ -12,7 +12,11 @@ import ServicesInitAnimations from "@/components/pages/services/ServicesInitAnim
import { Service } from "@/lib/api/serviceService"; import { Service } from "@/lib/api/serviceService";
import { generateServiceMetadata } from "@/lib/seo/metadata"; import { generateServiceMetadata } from "@/lib/seo/metadata";
import { ServiceSchema, BreadcrumbSchema } from "@/components/shared/seo/StructuredData"; import { ServiceSchema, BreadcrumbSchema } from "@/components/shared/seo/StructuredData";
<<<<<<< HEAD
import { API_CONFIG, getApiHeaders } from "@/lib/config/api"; import { API_CONFIG, getApiHeaders } from "@/lib/config/api";
=======
import { API_CONFIG } from "@/lib/config/api";
>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
interface ServicePageProps { interface ServicePageProps {
params: Promise<{ params: Promise<{
@@ -24,6 +28,7 @@ interface ServicePageProps {
// This pre-generates known pages, but new pages can still be generated on-demand // This pre-generates known pages, but new pages can still be generated on-demand
export async function generateStaticParams() { export async function generateStaticParams() {
try { try {
<<<<<<< HEAD
// Use internal API URL for server-side requests // 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 apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:1086';
const response = await fetch( const response = await fetch(
@@ -31,6 +36,15 @@ export async function generateStaticParams() {
{ {
method: 'GET', method: 'GET',
headers: getApiHeaders(), 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 next: { revalidate: 60 }, // Revalidate every minute for faster image updates
} }
); );
@@ -57,6 +71,7 @@ export async function generateMetadata({ params }: ServicePageProps) {
const { slug } = await params; const { slug } = await params;
try { try {
<<<<<<< HEAD
// Use internal API URL for server-side requests // 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 apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:1086';
const response = await fetch( const response = await fetch(
@@ -64,6 +79,15 @@ export async function generateMetadata({ params }: ServicePageProps) {
{ {
method: 'GET', method: 'GET',
headers: getApiHeaders(), 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 next: { revalidate: 60 }, // Revalidate every minute for faster image updates
} }
); );
@@ -87,6 +111,7 @@ const ServicePage = async ({ params }: ServicePageProps) => {
const { slug } = await params; const { slug } = await params;
try { try {
<<<<<<< HEAD
// Use internal API URL for server-side requests // 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 apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:1086';
const response = await fetch( const response = await fetch(
@@ -94,6 +119,15 @@ const ServicePage = async ({ params }: ServicePageProps) => {
{ {
method: 'GET', method: 'GET',
headers: getApiHeaders(), 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 next: { revalidate: 60 }, // Revalidate every minute for faster image updates
} }
); );

View File

@@ -12,8 +12,29 @@ type ModalType = 'create' | 'knowledge' | 'status' | null;
const SupportCenterPage = () => { const SupportCenterPage = () => {
// Set metadata for client component // Set metadata for client component
useEffect(() => { useEffect(() => {
<<<<<<< HEAD
// Only run on client side // Only run on client side
if (typeof window === 'undefined' || typeof document === 'undefined') return; 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 { try {
const metadata = createMetadata({ const metadata = createMetadata({

View File

@@ -227,7 +227,11 @@ const AboutBanner = () => {
{/* Social Links */} {/* Social Links */}
<div className="social-links"> <div className="social-links">
<<<<<<< HEAD
<Link href="https://www.linkedin.com" target="_blank" className="social-link"> <Link href="https://www.linkedin.com" target="_blank" className="social-link">
=======
<Link href="https://linkedin.com" target="_blank" className="social-link">
>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
<i className="fa-brands fa-linkedin-in"></i> <i className="fa-brands fa-linkedin-in"></i>
</Link> </Link>
<Link href="https://github.com" target="_blank" className="social-link"> <Link href="https://github.com" target="_blank" className="social-link">

View File

@@ -117,7 +117,11 @@ const CareerBanner = () => {
<ul className="social"> <ul className="social">
<li> <li>
<Link <Link
<<<<<<< HEAD
href="https://www.linkedin.com" href="https://www.linkedin.com"
=======
href="https://linkedin.com"
>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
target="_blank" target="_blank"
aria-label="connect with us on linkedin" aria-label="connect with us on linkedin"
> >

View File

@@ -5,7 +5,10 @@ import Link from "next/link";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useCaseStudy } from "@/lib/hooks/useCaseStudy"; import { useCaseStudy } from "@/lib/hooks/useCaseStudy";
import { getImageUrl } from "@/lib/imageUtils"; import { getImageUrl } from "@/lib/imageUtils";
<<<<<<< HEAD
import { sanitizeHTML } from "@/lib/security/sanitize"; import { sanitizeHTML } from "@/lib/security/sanitize";
=======
>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
interface CaseSingleProps { interface CaseSingleProps {
slug: string; slug: string;

View File

@@ -84,7 +84,11 @@ const ServicesBanner = () => {
<ul className="social"> <ul className="social">
<li> <li>
<Link <Link
<<<<<<< HEAD
href="https://www.linkedin.com" href="https://www.linkedin.com"
=======
href="https://linkedin.com"
>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
target="_blank" target="_blank"
aria-label="connect with us on linkedin" aria-label="connect with us on linkedin"
> >

View File

@@ -289,7 +289,11 @@ const Footer = () => {
<div className="col-12 col-lg-6"> <div className="col-12 col-lg-6">
<div className="social-links justify-content-center justify-content-lg-end"> <div className="social-links justify-content-center justify-content-lg-end">
<Link <Link
<<<<<<< HEAD
href="https://www.linkedin.com" href="https://www.linkedin.com"
=======
href="https://linkedin.com"
>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
title="LinkedIn" title="LinkedIn"
@@ -298,7 +302,11 @@ const Footer = () => {
<i className="fa-brands fa-linkedin-in"></i> <i className="fa-brands fa-linkedin-in"></i>
</Link> </Link>
<Link <Link
<<<<<<< HEAD
href="https://github.com/" href="https://github.com/"
=======
href="https://github.com"
>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
title="GitHub" title="GitHub"

View File

@@ -175,7 +175,11 @@ const OffcanvasMenu = ({
<ul className="enterprise-social nav-fade"> <ul className="enterprise-social nav-fade">
<li> <li>
<Link <Link
<<<<<<< HEAD
href="https://www.linkedin.com" href="https://www.linkedin.com"
=======
href="https://linkedin.com"
>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
target="_blank" target="_blank"
aria-label="Connect with us on LinkedIn" aria-label="Connect with us on LinkedIn"
> >

View File

@@ -123,9 +123,13 @@ const nextConfig = {
// Note: Removed conflicting directives that are ignored with 'strict-dynamic' // Note: Removed conflicting directives that are ignored with 'strict-dynamic'
{ {
key: 'Content-Security-Policy', key: 'Content-Security-Policy',
<<<<<<< HEAD
value: process.env.NODE_ENV === 'production' 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:; 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';" : "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 // Hide X-Powered-By header from Next.js
{ {

View File

@@ -11,6 +11,14 @@
# 3. Database: PostgreSQL on host (port 5433 to avoid conflict with Docker instance on 5432) # 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 # 4. Use install-postgresql.sh to install and configure PostgreSQL
# 5. Use start-services.sh to start both backend and frontend services # 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) # Frontend - Public facing (Next.js Production Server on port 1087)
upstream frontend { upstream frontend {
@@ -64,9 +72,7 @@ server {
add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()" always;
# Rate Limiting Zones # Rate Limiting (zones must be defined in main nginx.conf http context)
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;
limit_req_status 429; limit_req_status 429;
# Client settings # Client settings
@@ -78,7 +84,147 @@ server {
access_log /var/log/nginx/gnxsoft_access.log; access_log /var/log/nginx/gnxsoft_access.log;
error_log /var/log/nginx/gnxsoft_error.log warn; 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 / { location / {
limit_req zone=general_limit burst=50 nodelay; limit_req zone=general_limit burst=50 nodelay;
@@ -98,6 +244,7 @@ server {
proxy_read_timeout 60s; proxy_read_timeout 60s;
} }
<<<<<<< HEAD
# API Proxy - Frontend talks to backend ONLY through this internal proxy # API Proxy - Frontend talks to backend ONLY through this internal proxy
# Backend port 1086 is BLOCKED from internet by firewall # Backend port 1086 is BLOCKED from internet by firewall
location /api/ { location /api/ {
@@ -173,6 +320,8 @@ server {
access_log off; access_log off;
} }
=======
>>>>>>> d7d7a2757a183aa1abd9dbabff804c45298df4e5
# Deny access to hidden files # Deny access to hidden files
location ~ /\. { location ~ /\. {
deny all; deny all;
@@ -184,7 +333,11 @@ server {
location /admin/ { location /admin/ {
# IP restriction is handled by Django middleware # IP restriction is handled by Django middleware
# Add internal API key (must match INTERNAL_API_KEY in Django .env) # Add internal API key (must match INTERNAL_API_KEY in Django .env)
<<<<<<< HEAD
set $api_key "PLACEHOLDER_INTERNAL_API_KEY"; 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 X-Internal-API-Key $api_key;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
@@ -220,7 +373,10 @@ server {
# 5. Public internet can ONLY access nginx (ports 80, 443) # 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/*) # 6. All API calls go through nginx proxy (/api/* → 127.0.0.1:1086/api/*)
# 7. Backend IP whitelist middleware ensures only localhost requests # 7. Backend IP whitelist middleware ensures only localhost requests
<<<<<<< HEAD
# 8. Update PLACEHOLDER_INTERNAL_API_KEY with actual key from Django .env # 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) # 9. PostgreSQL runs on port 5433 (to avoid conflict with Docker on 5432)
# ============================================================================== # ==============================================================================

View File

@@ -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;

View File

@@ -211,10 +211,48 @@ else
fi fi
fi fi
<<<<<<< HEAD
# Set PORT environment variable and start with PM2 # Set PORT environment variable and start with PM2
PORT=$FRONTEND_PORT NODE_ENV=production pm2 start npm \ PORT=$FRONTEND_PORT NODE_ENV=production pm2 start npm \
--name "gnxsoft-frontend" \ --name "gnxsoft-frontend" \
-- start -- 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 # Save PM2 configuration
pm2 save pm2 save

34
switch-to-sqlite.sh Normal file
View File

@@ -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