update
This commit is contained in:
@@ -1,444 +0,0 @@
|
||||
# Policy API Setup - Complete Guide
|
||||
|
||||
## 🎉 Summary
|
||||
|
||||
Successfully created a comprehensive Policy Management System with:
|
||||
- **3 Policy Types**: Privacy Policy, Terms of Use, Support Policy
|
||||
- **Professional Enterprise Content**: 13-15 sections per policy with detailed legal and operational information
|
||||
- **Full CRUD API**: RESTful API endpoints for managing policies
|
||||
- **Dynamic Frontend**: React-based policy viewer with loading states and error handling
|
||||
|
||||
---
|
||||
|
||||
## 📋 What Was Created
|
||||
|
||||
### Backend (Django)
|
||||
|
||||
#### 1. **Models** (`backend/policies/models.py`)
|
||||
- `Policy`: Main policy document with metadata (type, title, version, effective date)
|
||||
- `PolicySection`: Individual sections within each policy
|
||||
|
||||
#### 2. **API Views** (`backend/policies/views.py`)
|
||||
- `PolicyViewSet`: RESTful viewset for policy CRUD operations
|
||||
- Endpoints support filtering by type
|
||||
- Retrieve by ID or policy type
|
||||
|
||||
#### 3. **Serializers** (`backend/policies/serializers.py`)
|
||||
- `PolicySerializer`: Full policy with all sections
|
||||
- `PolicyListSerializer`: Simplified list view
|
||||
- `PolicySectionSerializer`: Individual section data
|
||||
|
||||
#### 4. **Admin Interface** (`backend/policies/admin.py`)
|
||||
- Full admin panel integration
|
||||
- Inline section editing
|
||||
- List filters and search functionality
|
||||
|
||||
#### 5. **Management Command** (`backend/policies/management/commands/populate_policies.py`)
|
||||
- Command: `python manage.py populate_policies`
|
||||
- Creates/updates all 3 policies with professional content
|
||||
- 42 total sections across all policies
|
||||
|
||||
### Frontend (Next.js)
|
||||
|
||||
#### 1. **API Service** (`lib/api/policyService.ts`)
|
||||
- `getPolicies()`: Fetch all policies
|
||||
- `getPolicyByType(type)`: Fetch specific policy
|
||||
- `getPolicyById(id)`: Fetch by ID
|
||||
|
||||
#### 2. **React Hook** (`lib/hooks/usePolicy.ts`)
|
||||
- `usePolicies()`: Hook for all policies
|
||||
- `usePolicy(type)`: Hook for specific policy
|
||||
- `usePolicyById(id)`: Hook for policy by ID
|
||||
|
||||
#### 3. **Policy Page** (`app/policy/page.tsx`)
|
||||
- Dynamic page showing policy content
|
||||
- Query parameter: `?type=privacy|terms|support`
|
||||
- Loading and error states
|
||||
- Responsive design
|
||||
- Styled with inline styles
|
||||
|
||||
#### 4. **Support Center Integration** (`components/pages/support/SupportCenterHero.tsx`)
|
||||
- Added 3 new clickable cards:
|
||||
- Privacy Policy → `/policy?type=privacy`
|
||||
- Terms of Use → `/policy?type=terms`
|
||||
- Support Policy → `/policy?type=support`
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Setup Complete!
|
||||
|
||||
### What Was Done:
|
||||
|
||||
```bash
|
||||
# 1. Created migrations
|
||||
python manage.py makemigrations policies
|
||||
✅ Created: policies/migrations/0001_initial.py
|
||||
|
||||
# 2. Applied migrations
|
||||
python manage.py migrate policies
|
||||
✅ Created database tables
|
||||
|
||||
# 3. Populated with content
|
||||
python manage.py populate_policies
|
||||
✅ Privacy Policy: 13 sections
|
||||
✅ Terms of Use: 14 sections
|
||||
✅ Support Policy: 15 sections
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📡 API Endpoints
|
||||
|
||||
### Base URL
|
||||
```
|
||||
http://localhost:8000/api/policies/
|
||||
```
|
||||
|
||||
### Available Endpoints
|
||||
|
||||
#### 1. **List All Policies**
|
||||
```
|
||||
GET /api/policies/
|
||||
```
|
||||
**Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"type": "privacy",
|
||||
"title": "Privacy Policy",
|
||||
"slug": "privacy",
|
||||
"description": "Our commitment to protecting your privacy and personal data",
|
||||
"last_updated": "2025-10-08",
|
||||
"version": "2.1"
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
#### 2. **Get Specific Policy by Type**
|
||||
```
|
||||
GET /api/policies/privacy/
|
||||
GET /api/policies/terms/
|
||||
GET /api/policies/support/
|
||||
```
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"type": "privacy",
|
||||
"title": "Privacy Policy",
|
||||
"slug": "privacy",
|
||||
"description": "Our commitment to protecting your privacy and personal data",
|
||||
"last_updated": "2025-10-08",
|
||||
"version": "2.1",
|
||||
"effective_date": "2025-10-08",
|
||||
"sections": [
|
||||
{
|
||||
"id": 1,
|
||||
"heading": "1. Introduction and Scope",
|
||||
"content": "GNX Software Solutions...",
|
||||
"order": 1
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. **Get Policy by ID**
|
||||
```
|
||||
GET /api/policies/{id}/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Frontend Usage
|
||||
|
||||
### 1. **Direct Navigation**
|
||||
```typescript
|
||||
// Link to policies from anywhere
|
||||
<a href="/policy?type=privacy">Privacy Policy</a>
|
||||
<a href="/policy?type=terms">Terms of Use</a>
|
||||
<a href="/policy?type=support">Support Policy</a>
|
||||
```
|
||||
|
||||
### 2. **Using the Hook**
|
||||
```typescript
|
||||
import { usePolicy } from '@/lib/hooks/usePolicy';
|
||||
|
||||
function MyComponent() {
|
||||
const { data: policy, isLoading, error } = usePolicy('privacy');
|
||||
|
||||
if (isLoading) return <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{policy.title}</h1>
|
||||
{policy.sections.map(section => (
|
||||
<div key={section.id}>
|
||||
<h2>{section.heading}</h2>
|
||||
<p>{section.content}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. **Support Center Cards**
|
||||
The support center hero now has 6 cards:
|
||||
- **Top Row** (Opens Modals):
|
||||
- Submit Tickets
|
||||
- Knowledge Base
|
||||
- Track Status
|
||||
- **Bottom Row** (Navigates to Policy Page):
|
||||
- Privacy Policy
|
||||
- Terms of Use
|
||||
- Support Policy
|
||||
|
||||
---
|
||||
|
||||
## 📝 Policy Content Overview
|
||||
|
||||
### Privacy Policy (13 Sections)
|
||||
1. Introduction and Scope
|
||||
2. Information We Collect
|
||||
3. How We Collect Information
|
||||
4. Use of Information
|
||||
5. Information Sharing and Disclosure
|
||||
6. Data Security
|
||||
7. Data Retention
|
||||
8. Your Rights and Choices
|
||||
9. International Data Transfers
|
||||
10. Cookies and Tracking Technologies
|
||||
11. Children's Privacy
|
||||
12. Changes to This Privacy Policy
|
||||
13. Contact Information
|
||||
|
||||
### Terms of Use (14 Sections)
|
||||
1. Acceptance of Terms
|
||||
2. Service Description and Scope
|
||||
3. User Accounts and Registration
|
||||
4. Acceptable Use Policy
|
||||
5. Intellectual Property Rights
|
||||
6. Service Level Agreements
|
||||
7. Payment Terms and Billing
|
||||
8. Term and Termination
|
||||
9. Disclaimer of Warranties
|
||||
10. Limitation of Liability
|
||||
11. Dispute Resolution and Governing Law
|
||||
12. Modifications to Terms
|
||||
13. General Provisions
|
||||
14. Contact Information
|
||||
|
||||
### Support Policy (15 Sections)
|
||||
1. Support Overview and Commitment
|
||||
2. Support Coverage and Eligibility
|
||||
3. Support Channels and Access Methods
|
||||
4. Business Hours and Availability
|
||||
5. Priority Levels and Response Times
|
||||
6. Support Request Submission Requirements
|
||||
7. Support Scope and Covered Activities
|
||||
8. Exclusions and Limitations
|
||||
9. Escalation Procedures
|
||||
10. Knowledge Base and Self-Service Resources
|
||||
11. Customer Responsibilities
|
||||
12. Scheduled Maintenance and Updates
|
||||
13. Service Level Credits and Remedies
|
||||
14. Feedback and Continuous Improvement
|
||||
15. Contact Information and Resources
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Admin Panel
|
||||
|
||||
Access the admin panel to manage policies:
|
||||
|
||||
```
|
||||
http://localhost:8000/admin/policies/
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- ✅ Create/Edit/Delete policies
|
||||
- ✅ Inline section editing
|
||||
- ✅ Version management
|
||||
- ✅ Effective date tracking
|
||||
- ✅ Active/Inactive toggle
|
||||
- ✅ Search and filtering
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Features
|
||||
|
||||
### Backend
|
||||
- ✅ RESTful API with Django REST Framework
|
||||
- ✅ Versioned policies
|
||||
- ✅ Effective date tracking
|
||||
- ✅ Ordered sections
|
||||
- ✅ Active/Inactive status
|
||||
- ✅ Full admin interface
|
||||
- ✅ Management command for easy population
|
||||
|
||||
### Frontend
|
||||
- ✅ Dynamic policy loading from API
|
||||
- ✅ Loading states with spinner
|
||||
- ✅ Error handling with user-friendly messages
|
||||
- ✅ Responsive design (mobile-friendly)
|
||||
- ✅ Version and date display
|
||||
- ✅ Smooth animations
|
||||
- ✅ SEO-friendly structure
|
||||
- ✅ Integration with support center
|
||||
|
||||
---
|
||||
|
||||
## 🚦 Testing
|
||||
|
||||
### Test the API
|
||||
```bash
|
||||
# List all policies
|
||||
curl http://localhost:8000/api/policies/
|
||||
|
||||
# Get privacy policy
|
||||
curl http://localhost:8000/api/policies/privacy/
|
||||
|
||||
# Get terms of use
|
||||
curl http://localhost:8000/api/policies/terms/
|
||||
|
||||
# Get support policy
|
||||
curl http://localhost:8000/api/policies/support/
|
||||
```
|
||||
|
||||
### Test the Frontend
|
||||
1. Navigate to: `http://localhost:3000/policy?type=privacy`
|
||||
2. Navigate to: `http://localhost:3000/policy?type=terms`
|
||||
3. Navigate to: `http://localhost:3000/policy?type=support`
|
||||
4. Navigate to: `http://localhost:3000/support-center`
|
||||
5. Click on any of the 6 feature cards
|
||||
|
||||
---
|
||||
|
||||
## 📊 Database Schema
|
||||
|
||||
```sql
|
||||
-- Policy Table
|
||||
CREATE TABLE policies_policy (
|
||||
id INTEGER PRIMARY KEY,
|
||||
type VARCHAR(50) UNIQUE,
|
||||
title VARCHAR(200),
|
||||
slug VARCHAR(100) UNIQUE,
|
||||
description TEXT,
|
||||
last_updated DATE,
|
||||
version VARCHAR(20),
|
||||
is_active BOOLEAN,
|
||||
effective_date DATE,
|
||||
created_at TIMESTAMP,
|
||||
updated_at TIMESTAMP
|
||||
);
|
||||
|
||||
-- PolicySection Table
|
||||
CREATE TABLE policies_policysection (
|
||||
id INTEGER PRIMARY KEY,
|
||||
policy_id INTEGER REFERENCES policies_policy(id),
|
||||
heading VARCHAR(300),
|
||||
content TEXT,
|
||||
order INTEGER,
|
||||
is_active BOOLEAN,
|
||||
created_at TIMESTAMP,
|
||||
updated_at TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Updating Policies
|
||||
|
||||
### Via Management Command
|
||||
```bash
|
||||
# Re-run to update all policies
|
||||
cd backend
|
||||
../venv/bin/python manage.py populate_policies
|
||||
```
|
||||
|
||||
### Via Admin Panel
|
||||
1. Go to `http://localhost:8000/admin/policies/policy/`
|
||||
2. Click on the policy to edit
|
||||
3. Modify sections inline
|
||||
4. Save changes
|
||||
|
||||
### Via API (Programmatic)
|
||||
```python
|
||||
from policies.models import Policy, PolicySection
|
||||
|
||||
# Get policy
|
||||
policy = Policy.objects.get(type='privacy')
|
||||
|
||||
# Add new section
|
||||
PolicySection.objects.create(
|
||||
policy=policy,
|
||||
heading="New Section",
|
||||
content="Section content...",
|
||||
order=14
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Customization
|
||||
|
||||
### Styling
|
||||
The policy page uses inline styles. To customize:
|
||||
- Edit `/app/policy/page.tsx`
|
||||
- Modify the `<style jsx>` blocks
|
||||
- Or create a separate SCSS file
|
||||
|
||||
### Content
|
||||
To modify policy content:
|
||||
1. Edit `/backend/policies/management/commands/populate_policies.py`
|
||||
2. Update the sections array for each policy
|
||||
3. Run: `python manage.py populate_policies`
|
||||
|
||||
---
|
||||
|
||||
## ✅ Checklist
|
||||
|
||||
- [x] Backend models created
|
||||
- [x] Database migrations applied
|
||||
- [x] API endpoints configured
|
||||
- [x] Professional content populated
|
||||
- [x] Frontend service created
|
||||
- [x] React hooks implemented
|
||||
- [x] Policy page created
|
||||
- [x] Support center integrated
|
||||
- [x] Loading states added
|
||||
- [x] Error handling implemented
|
||||
- [x] Responsive design
|
||||
- [x] Admin panel configured
|
||||
|
||||
---
|
||||
|
||||
## 🎉 You're All Set!
|
||||
|
||||
The Policy API system is now fully operational. Users can:
|
||||
1. View all three policies from the support center
|
||||
2. Access detailed, professional legal documents
|
||||
3. See version information and effective dates
|
||||
4. Navigate easily between policies
|
||||
|
||||
**Next Steps:**
|
||||
- Customize policy content for your organization
|
||||
- Add more policy types if needed
|
||||
- Integrate policy acceptance tracking (optional)
|
||||
- Add PDF export functionality (optional)
|
||||
|
||||
---
|
||||
|
||||
## 📞 Support
|
||||
|
||||
For questions or issues:
|
||||
- Check the Django admin: `http://localhost:8000/admin/`
|
||||
- View API docs: `http://localhost:8000/swagger/`
|
||||
- Review backend logs: `/backend/logs/django.log`
|
||||
|
||||
**Congratulations!** 🎊 Your enterprise-grade policy management system is ready to use!
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
# Support Center Modal Implementation
|
||||
|
||||
## Overview
|
||||
The support center has been updated to hide forms by default and show them in modal popups when users click on the hero section feature items.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. **Main Page Component** (`app/support-center/page.tsx`)
|
||||
- Converted to a client component with state management
|
||||
- Added `activeModal` state to track which modal should be displayed
|
||||
- Props passed to child components to control modal visibility
|
||||
|
||||
### 2. **Hero Component** (`components/pages/support/SupportCenterHero.tsx`)
|
||||
- Added `onFeatureClick` prop to handle feature item clicks
|
||||
- Made all three feature items clickable:
|
||||
- **Submit Tickets** - Opens the Create Ticket form
|
||||
- **Knowledge Base** - Opens the Knowledge Base
|
||||
- **Track Status** - Opens the Ticket Status Check form
|
||||
- Added proper accessibility attributes (`role="button"`, `tabIndex`, keyboard support)
|
||||
- Added `clickable` class for enhanced styling
|
||||
|
||||
### 3. **Content Component** (`components/pages/support/SupportCenterContent.tsx`)
|
||||
- Completely redesigned as a modal overlay system
|
||||
- Forms are now hidden by default and only shown when `activeModal` is set
|
||||
- Features:
|
||||
- **Full-screen modal overlay** with backdrop blur
|
||||
- **Smooth animations** (slide-in effect)
|
||||
- **Close functionality**:
|
||||
- Click overlay to close
|
||||
- Click X button to close
|
||||
- Press ESC key to close
|
||||
- **Body scroll prevention** when modal is open
|
||||
- **Custom scrollbar styling** for better UX
|
||||
- **Responsive design** with mobile optimizations
|
||||
|
||||
### 4. **Styling Updates** (`public/styles/pages/_support-center.scss`)
|
||||
- Enhanced `.feature-item.clickable` styling:
|
||||
- Cursor pointer
|
||||
- Enhanced hover effects (lift higher, glow effect)
|
||||
- Active state for click feedback
|
||||
- Box shadow on hover with gold accent
|
||||
|
||||
## User Experience Flow
|
||||
|
||||
1. User lands on support center page
|
||||
2. Sees hero section with three feature cards (no forms visible)
|
||||
3. Clicks on any feature card:
|
||||
- "Submit Tickets" → Opens ticket creation form in modal
|
||||
- "Knowledge Base" → Opens knowledge base in modal
|
||||
- "Track Status" → Opens ticket status checker in modal
|
||||
4. Modal appears with smooth animation
|
||||
5. User can:
|
||||
- Interact with the form/content
|
||||
- Close via X button, ESC key, or clicking outside
|
||||
6. Modal closes with smooth animation
|
||||
7. User returns to clean hero view
|
||||
|
||||
## Benefits
|
||||
|
||||
✅ **Cleaner UI** - Hero section is not cluttered with forms
|
||||
✅ **Better Focus** - Modals help users focus on one task at a time
|
||||
✅ **Improved UX** - Clear call-to-actions with visual feedback
|
||||
✅ **Accessibility** - Keyboard navigation and proper ARIA attributes
|
||||
✅ **Mobile Friendly** - Responsive modal design works on all devices
|
||||
✅ **Performance** - Forms only render when needed
|
||||
|
||||
## Technical Features
|
||||
|
||||
- TypeScript type safety throughout
|
||||
- React hooks for state management
|
||||
- Event handling (click, keyboard, ESC)
|
||||
- CSS-in-JS for modal styling (with Next.js styled-jsx)
|
||||
- Smooth CSS animations
|
||||
- Responsive breakpoints
|
||||
- Accessibility support
|
||||
- Body scroll lock when modal open
|
||||
|
||||
## Browser Support
|
||||
|
||||
Works on all modern browsers with:
|
||||
- CSS backdrop-filter
|
||||
- CSS animations
|
||||
- Flexbox
|
||||
- Modern JavaScript (ES6+)
|
||||
|
||||
@@ -1,452 +0,0 @@
|
||||
# Support Center Implementation - Complete Setup Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The Enterprise Support Center has been successfully implemented! This comprehensive system allows users to:
|
||||
|
||||
1. **Submit Support Tickets** - Create and track support requests
|
||||
2. **Browse Knowledge Base** - Search and read helpful articles
|
||||
3. **Check Ticket Status** - Monitor ticket progress using ticket numbers
|
||||
4. **No Authentication Required** - Public access to support features
|
||||
|
||||
---
|
||||
|
||||
## ✅ What's Been Implemented
|
||||
|
||||
### Backend (Django)
|
||||
|
||||
#### 1. **Models** (`backend/support/models.py`)
|
||||
- `TicketStatus` - Track ticket lifecycle (Open, In Progress, Resolved, etc.)
|
||||
- `TicketPriority` - Priority levels (Low, Medium, High, Critical) with SLA hours
|
||||
- `TicketCategory` - Organize tickets by category
|
||||
- `SupportTicket` - Main ticket model with auto-generated ticket numbers
|
||||
- `TicketMessage` - Conversation history on tickets
|
||||
- `TicketActivity` - Audit trail of all changes
|
||||
- `KnowledgeBaseCategory` - KB article categories
|
||||
- `KnowledgeBaseArticle` - Help articles with view counts and feedback
|
||||
- `SupportSettings` - Configurable support center settings
|
||||
|
||||
#### 2. **API Endpoints** (`backend/support/views.py`)
|
||||
|
||||
**Tickets:**
|
||||
- `POST /api/support/tickets/` - Create new ticket
|
||||
- `GET /api/support/tickets/` - List tickets
|
||||
- `POST /api/support/tickets/check-status/` - Check status by ticket number
|
||||
- `POST /api/support/tickets/{id}/add-message/` - Add message to ticket
|
||||
|
||||
**Categories & Settings:**
|
||||
- `GET /api/support/categories/` - List ticket categories
|
||||
- `GET /api/support/statuses/` - List ticket statuses
|
||||
- `GET /api/support/priorities/` - List priorities
|
||||
|
||||
**Knowledge Base:**
|
||||
- `GET /api/support/knowledge-base/` - List articles (with search)
|
||||
- `GET /api/support/knowledge-base/{slug}/` - Get article details
|
||||
- `GET /api/support/knowledge-base/featured/` - Featured articles
|
||||
- `POST /api/support/knowledge-base/{slug}/mark-helpful/` - Rate article
|
||||
|
||||
#### 3. **Admin Panel** (`backend/support/admin.py`)
|
||||
Full Django admin integration for managing:
|
||||
- Tickets with inline messages and activities
|
||||
- Categories, statuses, and priorities
|
||||
- Knowledge base articles and categories
|
||||
- Support settings
|
||||
|
||||
#### 4. **Management Command**
|
||||
```bash
|
||||
python manage.py populate_support_data
|
||||
```
|
||||
Populates initial data:
|
||||
- 5 ticket statuses
|
||||
- 4 priority levels
|
||||
- 6 ticket categories
|
||||
- 6 KB categories
|
||||
- 6 sample KB articles
|
||||
|
||||
---
|
||||
|
||||
### Frontend (Next.js/React)
|
||||
|
||||
#### 1. **Page Route**
|
||||
- `/support-center` - Main support center page
|
||||
|
||||
#### 2. **Components** (`components/pages/support/`)
|
||||
|
||||
**Main Components:**
|
||||
- `SupportCenterHero.tsx` - Hero section with feature highlights
|
||||
- `SupportCenterContent.tsx` - Tabbed interface container
|
||||
- `CreateTicketForm.tsx` - Ticket submission form
|
||||
- `TicketStatusCheck.tsx` - Check ticket progress
|
||||
- `KnowledgeBase.tsx` - Browse and search articles
|
||||
- `KnowledgeBaseArticleModal.tsx` - Article detail modal
|
||||
|
||||
#### 3. **API Services** (`lib/api/supportService.ts`)
|
||||
TypeScript API client with functions for:
|
||||
- Creating tickets
|
||||
- Checking status
|
||||
- Managing KB articles
|
||||
- All CRUD operations
|
||||
|
||||
#### 4. **Custom Hooks** (`lib/hooks/useSupport.ts`)
|
||||
React hooks for data fetching:
|
||||
- `useTicketCategories()`
|
||||
- `useKnowledgeBaseCategories()`
|
||||
- `useFeaturedArticles()`
|
||||
- `useKnowledgeBaseArticle(slug)`
|
||||
- And more...
|
||||
|
||||
#### 5. **Styling** (`public/styles/pages/_support-center.scss`)
|
||||
Complete, modern styling including:
|
||||
- Gradient hero section
|
||||
- Tabbed interface
|
||||
- Responsive forms
|
||||
- Article cards and modals
|
||||
- Loading and error states
|
||||
- Mobile-responsive design
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
### Backend Setup
|
||||
|
||||
1. **Navigate to backend:**
|
||||
```bash
|
||||
cd backend
|
||||
```
|
||||
|
||||
2. **Database is already migrated and populated!** ✅
|
||||
- Migrations applied
|
||||
- Initial data loaded
|
||||
|
||||
3. **Start Django server:**
|
||||
```bash
|
||||
../venv/bin/python manage.py runserver
|
||||
```
|
||||
|
||||
4. **Access Admin Panel:**
|
||||
- URL: http://localhost:8000/admin
|
||||
- Create a superuser if needed:
|
||||
```bash
|
||||
../venv/bin/python manage.py createsuperuser
|
||||
```
|
||||
|
||||
### Frontend Setup
|
||||
|
||||
1. **Navigate to project root:**
|
||||
```bash
|
||||
cd /home/gnx/Desktop/GNX-WEB/gnx-react
|
||||
```
|
||||
|
||||
2. **Install dependencies (if not already):**
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
3. **Start development server:**
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
4. **Access Support Center:**
|
||||
- URL: http://localhost:3000/support-center
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Key Features
|
||||
|
||||
### 1. Submit Tickets
|
||||
- **Form Fields:**
|
||||
- Personal info (name, email, phone, company)
|
||||
- Issue type (technical, billing, feature request, etc.)
|
||||
- Category selection
|
||||
- Subject and detailed description
|
||||
|
||||
- **Auto-Generated Ticket Numbers:**
|
||||
- Format: `TKT-YYYYMMDD-XXXXX`
|
||||
- Example: `TKT-20251007-A3B9C`
|
||||
|
||||
- **Success Confirmation:**
|
||||
- Displays ticket number
|
||||
- Guidance to save for tracking
|
||||
|
||||
### 2. Knowledge Base
|
||||
- **Search Functionality:**
|
||||
- Real-time search across articles
|
||||
- Search by title, content, keywords
|
||||
|
||||
- **Category Browser:**
|
||||
- 6 pre-populated categories
|
||||
- Color-coded icons
|
||||
- Article count per category
|
||||
|
||||
- **Featured Articles:**
|
||||
- Highlighted important articles
|
||||
- View counts
|
||||
- Helpful/not helpful ratings
|
||||
|
||||
- **Article Modal:**
|
||||
- Full article content
|
||||
- Rich text support
|
||||
- Feedback buttons
|
||||
- Article metadata
|
||||
|
||||
### 3. Ticket Status Checker
|
||||
- **Check by Ticket Number:**
|
||||
- Enter ticket number
|
||||
- View complete ticket details
|
||||
|
||||
- **Displays:**
|
||||
- Current status with color coding
|
||||
- Priority level
|
||||
- Creation and update dates
|
||||
- Full description
|
||||
- Message history
|
||||
- Activity timeline
|
||||
|
||||
### 4. Navigation Update
|
||||
- **Navbar Button Changed:**
|
||||
- Old: "Get Started" → `/contact-us`
|
||||
- New: "Support Center" → `/support-center`
|
||||
|
||||
---
|
||||
|
||||
## 📊 Database Schema
|
||||
|
||||
### Pre-Populated Data
|
||||
|
||||
**Ticket Statuses:**
|
||||
- Open (Blue)
|
||||
- In Progress (Orange)
|
||||
- Pending Response (Purple)
|
||||
- Resolved (Green)
|
||||
- Closed (Gray)
|
||||
|
||||
**Priorities:**
|
||||
- Critical (4 hours SLA)
|
||||
- High (24 hours SLA)
|
||||
- Medium (48 hours SLA)
|
||||
- Low (72 hours SLA)
|
||||
|
||||
**Ticket Categories:**
|
||||
- Technical Support
|
||||
- Billing & Payments
|
||||
- Account Management
|
||||
- Product Inquiry
|
||||
- Bug Reports
|
||||
- Feature Requests
|
||||
|
||||
**Knowledge Base Categories:**
|
||||
- Getting Started
|
||||
- Account & Billing
|
||||
- Technical Documentation
|
||||
- Troubleshooting
|
||||
- Security & Privacy
|
||||
- Best Practices
|
||||
|
||||
**Sample Articles:**
|
||||
- How to Get Started with Our Platform
|
||||
- Understanding Your Billing Cycle
|
||||
- API Documentation Overview
|
||||
- Common Login Issues and Solutions
|
||||
- How to Update Your Payment Method
|
||||
- Security Best Practices
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
### API Base URL
|
||||
Located in: `lib/config/api.ts`
|
||||
- Default: `http://localhost:8000/api`
|
||||
|
||||
### Django Settings
|
||||
Support app added to `INSTALLED_APPS` in `backend/gnx/settings.py`
|
||||
|
||||
### URLs
|
||||
Support endpoints registered in `backend/gnx/urls.py`:
|
||||
```python
|
||||
path('support/', include('support.urls')),
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 UI/UX Features
|
||||
|
||||
### Design Elements
|
||||
- **Modern Gradient Hero** - Dark theme with gold accents
|
||||
- **Tabbed Interface** - Easy navigation between features
|
||||
- **Responsive Design** - Mobile-first approach
|
||||
- **Loading States** - Spinners for async operations
|
||||
- **Error Handling** - User-friendly error messages
|
||||
- **Success Feedback** - Confirmation messages
|
||||
- **Color-Coded Status** - Visual ticket status indicators
|
||||
|
||||
### Animations
|
||||
- GSAP scroll animations on hero
|
||||
- Smooth tab transitions
|
||||
- Modal fade-in/slide-up effects
|
||||
- Hover effects on cards and buttons
|
||||
|
||||
---
|
||||
|
||||
## 📝 Usage Examples
|
||||
|
||||
### Create a Ticket (API)
|
||||
```javascript
|
||||
import { createTicket } from '@/lib/api/supportService';
|
||||
|
||||
const ticketData = {
|
||||
title: "Login issues on mobile app",
|
||||
description: "Cannot login using my credentials...",
|
||||
ticket_type: "technical",
|
||||
user_name: "John Doe",
|
||||
user_email: "john@example.com",
|
||||
user_phone: "+1234567890",
|
||||
company: "Acme Corp",
|
||||
category: 1 // Technical Support
|
||||
};
|
||||
|
||||
const ticket = await createTicket(ticketData);
|
||||
console.log(ticket.ticket_number); // TKT-20251007-A3B9C
|
||||
```
|
||||
|
||||
### Check Ticket Status (API)
|
||||
```javascript
|
||||
import { checkTicketStatus } from '@/lib/api/supportService';
|
||||
|
||||
const ticket = await checkTicketStatus('TKT-20251007-A3B9C');
|
||||
console.log(ticket.status_name); // "Open"
|
||||
```
|
||||
|
||||
### Search Knowledge Base (API)
|
||||
```javascript
|
||||
import { getKnowledgeBaseArticles } from '@/lib/api/supportService';
|
||||
|
||||
const articles = await getKnowledgeBaseArticles('login');
|
||||
articles.forEach(article => {
|
||||
console.log(article.title);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Security Notes
|
||||
|
||||
- **Public Access:** All endpoints are public (no authentication required)
|
||||
- **Ticket Numbers:** Randomly generated to prevent guessing
|
||||
- **Internal Notes:** Hidden from public API responses
|
||||
- **Rate Limiting:** Recommended for production deployment
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Future Enhancements
|
||||
|
||||
Potential additions for future development:
|
||||
|
||||
- [ ] **Live Chat Integration** - Real-time customer support
|
||||
- [ ] **File Attachments** - Upload files with tickets
|
||||
- [ ] **Email Notifications** - Automated ticket updates
|
||||
- [ ] **Ticket Assignment** - Auto-routing to support agents
|
||||
- [ ] **SLA Alerts** - Breach notifications
|
||||
- [ ] **Analytics Dashboard** - Support metrics and reports
|
||||
- [ ] **Multi-language Support** - Internationalization
|
||||
- [ ] **Webhook Notifications** - Integration with external systems
|
||||
- [ ] **Customer Portal** - User authentication for ticket management
|
||||
- [ ] **Advanced Search** - Filters, sorting, faceted search
|
||||
|
||||
---
|
||||
|
||||
## 📞 Testing the Implementation
|
||||
|
||||
### Test Ticket Creation:
|
||||
1. Go to http://localhost:3000/support-center
|
||||
2. Click "Submit a Ticket" tab
|
||||
3. Fill out the form
|
||||
4. Submit and note the ticket number
|
||||
|
||||
### Test Status Check:
|
||||
1. Click "Check Ticket Status" tab
|
||||
2. Enter the ticket number from above
|
||||
3. View complete ticket details
|
||||
|
||||
### Test Knowledge Base:
|
||||
1. Click "Knowledge Base" tab
|
||||
2. Browse categories or use search
|
||||
3. Click an article to view details
|
||||
4. Rate article as helpful/not helpful
|
||||
|
||||
### Test Admin Panel:
|
||||
1. Go to http://localhost:8000/admin
|
||||
2. Navigate to Support section
|
||||
3. View/edit tickets, categories, articles
|
||||
|
||||
---
|
||||
|
||||
## 📄 File Structure
|
||||
|
||||
```
|
||||
gnx-react/
|
||||
├── app/
|
||||
│ └── support-center/
|
||||
│ └── page.tsx # Main support page
|
||||
├── components/
|
||||
│ └── pages/
|
||||
│ └── support/
|
||||
│ ├── SupportCenterHero.tsx
|
||||
│ ├── SupportCenterContent.tsx
|
||||
│ ├── CreateTicketForm.tsx
|
||||
│ ├── TicketStatusCheck.tsx
|
||||
│ ├── KnowledgeBase.tsx
|
||||
│ └── KnowledgeBaseArticleModal.tsx
|
||||
├── lib/
|
||||
│ ├── api/
|
||||
│ │ └── supportService.ts # API client
|
||||
│ └── hooks/
|
||||
│ └── useSupport.ts # React hooks
|
||||
├── public/
|
||||
│ └── styles/
|
||||
│ └── pages/
|
||||
│ └── _support-center.scss # Styles
|
||||
└── backend/
|
||||
└── support/
|
||||
├── models.py # Database models
|
||||
├── serializers.py # API serializers
|
||||
├── views.py # API views
|
||||
├── urls.py # URL routing
|
||||
├── admin.py # Admin config
|
||||
├── management/
|
||||
│ └── commands/
|
||||
│ └── populate_support_data.py
|
||||
└── README.md # Module docs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✨ Summary
|
||||
|
||||
The Enterprise Support Center is now fully operational with:
|
||||
|
||||
✅ **Backend:** Complete Django REST API with 8 models and full CRUD operations
|
||||
✅ **Frontend:** Modern React/Next.js interface with 6 components
|
||||
✅ **Database:** Migrated and populated with sample data
|
||||
✅ **Styling:** Beautiful, responsive SCSS styles
|
||||
✅ **Navigation:** Updated navbar button
|
||||
✅ **Documentation:** Comprehensive README and guides
|
||||
|
||||
**You're ready to provide enterprise-level support to your customers!** 🎉
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Need Help?
|
||||
|
||||
If you encounter any issues:
|
||||
|
||||
1. Check that both Django and Next.js servers are running
|
||||
2. Verify the API base URL in `lib/config/api.ts`
|
||||
3. Check browser console for errors
|
||||
4. Review Django logs for backend issues
|
||||
5. Ensure all migrations are applied
|
||||
|
||||
Enjoy your new Support Center! 🚀
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client";
|
||||
import { useEffect } from 'react';
|
||||
import Header from "@/components/shared/layout/header/Header";
|
||||
import AboutBanner from "@/components/pages/about/AboutBanner";
|
||||
import AboutServiceComponent from "@/components/pages/about/AboutService";
|
||||
@@ -7,7 +8,15 @@ import AboutScrollProgressButton from "@/components/pages/about/AboutScrollProgr
|
||||
import AboutInitAnimations from "@/components/pages/about/AboutInitAnimations";
|
||||
import AboutStarter from "@/components/pages/about/AboutStarter";
|
||||
|
||||
// Note: Since this is a client component, we'll set metadata via useEffect
|
||||
const page = () => {
|
||||
useEffect(() => {
|
||||
document.title = "About Us - Enterprise Software Development Company | GNX Soft";
|
||||
const metaDescription = document.querySelector('meta[name="description"]');
|
||||
if (metaDescription) {
|
||||
metaDescription.setAttribute('content', 'Learn about GNX Soft - a leading enterprise software development company with expertise in custom software, data replication, AI business intelligence, and comprehensive IT solutions.');
|
||||
}
|
||||
}, []);
|
||||
return (
|
||||
<div className="enterprise-about-page">
|
||||
<Header />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Metadata } from 'next';
|
||||
import Header from "@/components/shared/layout/header/Header";
|
||||
import CareerBanner from "@/components/pages/career/CareerBanner";
|
||||
import OpenPosition from "@/components/pages/career/OpenPosition";
|
||||
@@ -5,6 +6,21 @@ import Thrive from "@/components/pages/career/Thrive";
|
||||
import Footer from "@/components/shared/layout/footer/Footer";
|
||||
import CareerScrollProgressButton from "@/components/pages/career/CareerScrollProgressButton";
|
||||
import CareerInitAnimations from "@/components/pages/career/CareerInitAnimations";
|
||||
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
|
||||
|
||||
export const metadata: Metadata = createMetadata({
|
||||
title: "Careers - Join Our Team",
|
||||
description: "Explore career opportunities at GNX Soft. Join our team of talented professionals working on cutting-edge enterprise software solutions. View open positions and apply today.",
|
||||
keywords: [
|
||||
"Careers",
|
||||
"Job Openings",
|
||||
"Software Development Jobs",
|
||||
"Join Our Team",
|
||||
"Tech Careers",
|
||||
"Employment Opportunities",
|
||||
],
|
||||
url: "/career",
|
||||
});
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
import { Metadata } from 'next';
|
||||
import Header from "@/components/shared/layout/header/Header";
|
||||
import CaseItems from "@/components/pages/case-study/CaseItems";
|
||||
import Footer from "@/components/shared/layout/footer/Footer";
|
||||
import CaseStudyScrollProgressButton from "@/components/pages/case-study/CaseStudyScrollProgressButton";
|
||||
import CaseStudyInitAnimations from "@/components/pages/case-study/CaseStudyInitAnimations";
|
||||
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
|
||||
|
||||
export const metadata: Metadata = createMetadata({
|
||||
title: "Case Studies - Success Stories & Client Projects",
|
||||
description: "Explore our case studies showcasing successful enterprise software development projects, client success stories, and real-world implementations of our technology solutions.",
|
||||
keywords: [
|
||||
"Case Studies",
|
||||
"Success Stories",
|
||||
"Client Projects",
|
||||
"Software Development Portfolio",
|
||||
"Enterprise Solutions Examples",
|
||||
"Client Testimonials",
|
||||
],
|
||||
url: "/case-study",
|
||||
});
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
import { Metadata } from 'next';
|
||||
import Header from "@/components/shared/layout/header/Header";
|
||||
import ContactSection from "@/components/pages/contact/ContactSection";
|
||||
import Footer from "@/components/shared/layout/footer/Footer";
|
||||
import ContactScrollProgressButton from "@/components/pages/contact/ContactScrollProgressButton";
|
||||
import ContactInitAnimations from "@/components/pages/contact/ContactInitAnimations";
|
||||
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
|
||||
|
||||
export const metadata: Metadata = createMetadata({
|
||||
title: "Contact Us - Get in Touch with Our Team",
|
||||
description: "Contact GNX Soft for enterprise software development solutions. Get a free consultation, discuss your project requirements, or request a quote for our services.",
|
||||
keywords: [
|
||||
"Contact GNX Soft",
|
||||
"Software Development Quote",
|
||||
"Enterprise Solutions Consultation",
|
||||
"Custom Software Inquiry",
|
||||
"Get in Touch",
|
||||
],
|
||||
url: "/contact-us",
|
||||
});
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
import { Metadata } from 'next';
|
||||
import Header from "@/components/shared/layout/header/Header";
|
||||
import BlogItems from "@/components/pages/blog/BlogItems";
|
||||
import Footer from "@/components/shared/layout/footer/Footer";
|
||||
import BlogScrollProgressButton from "@/components/pages/blog/BlogScrollProgressButton";
|
||||
import BlogInitAnimations from "@/components/pages/blog/BlogInitAnimations";
|
||||
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
|
||||
|
||||
export const metadata: Metadata = createMetadata({
|
||||
title: "Insights & Blog - Technology Trends & Best Practices",
|
||||
description: "Stay updated with the latest insights on enterprise software development, technology trends, best practices, and industry news from GNX Soft's expert team.",
|
||||
keywords: [
|
||||
"Technology Blog",
|
||||
"Software Development Insights",
|
||||
"Tech Trends",
|
||||
"Enterprise Software Blog",
|
||||
"Development Best Practices",
|
||||
"Industry News",
|
||||
],
|
||||
url: "/insights",
|
||||
});
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
|
||||
@@ -4,6 +4,8 @@ import "@/public/styles/main.scss";
|
||||
import { CookieConsentProvider } from "@/components/shared/layout/CookieConsentContext";
|
||||
import { CookieConsent } from "@/components/shared/layout/CookieConsent";
|
||||
import LayoutWrapper from "@/components/shared/layout/LayoutWrapper";
|
||||
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
|
||||
import { OrganizationSchema, WebsiteSchema, LocalBusinessSchema } from "@/components/shared/seo/StructuredData";
|
||||
|
||||
const montserrat = Montserrat({
|
||||
subsets: ["latin"],
|
||||
@@ -37,26 +39,22 @@ const inter = Inter({
|
||||
],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "EnterpriseSoft Solutions | Enterprise Software Development & IT Solutions",
|
||||
description: "Leading enterprise software development company providing custom solutions, system integrations, and digital transformation services for Fortune 500 companies",
|
||||
// Enhanced SEO metadata for root layout
|
||||
export const metadata: Metadata = createMetadata({
|
||||
title: "Enterprise Software Development & IT Solutions",
|
||||
description: "Leading enterprise software development company specializing in custom software, data replication, incident management, AI business intelligence, and comprehensive system integrations for modern businesses.",
|
||||
keywords: [
|
||||
"Enterprise Software",
|
||||
"Custom Development",
|
||||
"System Integration",
|
||||
"Digital Transformation",
|
||||
"Enterprise Solutions",
|
||||
"Software Consulting",
|
||||
"API Development",
|
||||
"Cloud Migration",
|
||||
"Enterprise Software Development",
|
||||
"Custom Software Solutions",
|
||||
"Data Replication Services",
|
||||
"Incident Management SaaS",
|
||||
"AI Business Intelligence",
|
||||
"Backend Engineering",
|
||||
"Frontend Engineering",
|
||||
"Systems Integration",
|
||||
],
|
||||
authors: [
|
||||
{
|
||||
name: "EnterpriseSoft Solutions",
|
||||
url: "https://enterprisesoft.com",
|
||||
},
|
||||
],
|
||||
};
|
||||
url: "/",
|
||||
});
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
@@ -76,14 +74,85 @@ export default function RootLayout({
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
{/* Content Protection Script */}
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
(function() {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
// Wait for DOM to be ready
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Disable right-click
|
||||
document.addEventListener('contextmenu', function(e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Disable keyboard shortcuts
|
||||
document.addEventListener('keydown', function(e) {
|
||||
// Ctrl+C, Ctrl+X, Ctrl+S, Ctrl+A, Ctrl+P, Ctrl+U, Ctrl+I, Ctrl+J
|
||||
if ((e.ctrlKey || e.metaKey) && ['c','x','s','a','p','u','i','j','k'].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
// F12
|
||||
if (e.key === 'F12' || e.keyCode === 123) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
// Ctrl+Shift+I, Ctrl+Shift+J, Ctrl+Shift+C
|
||||
if ((e.ctrlKey || e.metaKey) && e.shiftKey && ['I','J','C'].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Disable text selection
|
||||
document.addEventListener('selectstart', function(e) {
|
||||
if (e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Disable image dragging
|
||||
document.addEventListener('dragstart', function(e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Disable copy/cut
|
||||
document.addEventListener('copy', function(e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
document.addEventListener('cut', function(e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Console warning
|
||||
console.log('%cSTOP!', 'color: red; font-size: 40px; font-weight: bold;');
|
||||
console.log('%c© GNX Soft - All Rights Reserved', 'font-size: 14px;');
|
||||
});
|
||||
})();
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
<body className={`${inter.variable} ${montserrat.variable}`} style={{ scrollBehavior: 'auto', overflow: 'auto' }}>
|
||||
<body className={`${inter.variable} ${montserrat.variable} content-protected`} style={{ scrollBehavior: 'auto', overflow: 'auto' }}>
|
||||
{/* Structured Data for SEO */}
|
||||
<OrganizationSchema />
|
||||
<WebsiteSchema />
|
||||
<LocalBusinessSchema />
|
||||
|
||||
<CookieConsentProvider
|
||||
config={{
|
||||
companyName: "EnterpriseSoft Solutions",
|
||||
privacyPolicyUrl: "/privacy-policy",
|
||||
cookiePolicyUrl: "/cookie-policy",
|
||||
dataControllerEmail: "privacy@enterprisesoft.com",
|
||||
companyName: "GNX Soft",
|
||||
privacyPolicyUrl: "/policy",
|
||||
cookiePolicyUrl: "/policy",
|
||||
dataControllerEmail: "privacy@gnxsoft.com",
|
||||
retentionPeriod: 365,
|
||||
enableAuditLog: true,
|
||||
enableDetailedSettings: true,
|
||||
|
||||
35
gnx-react/app/robots.ts
Normal file
35
gnx-react/app/robots.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://gnxsoft.com';
|
||||
|
||||
return {
|
||||
rules: [
|
||||
{
|
||||
userAgent: '*',
|
||||
allow: '/',
|
||||
disallow: [
|
||||
'/api/',
|
||||
'/admin/',
|
||||
'/_next/',
|
||||
'/private/',
|
||||
'/*.json$',
|
||||
'/*?*',
|
||||
],
|
||||
},
|
||||
{
|
||||
userAgent: 'Googlebot',
|
||||
allow: '/',
|
||||
disallow: ['/api/', '/admin/', '/private/'],
|
||||
},
|
||||
{
|
||||
userAgent: 'Bingbot',
|
||||
allow: '/',
|
||||
disallow: ['/api/', '/admin/', '/private/'],
|
||||
},
|
||||
],
|
||||
sitemap: `${baseUrl}/sitemap.xml`,
|
||||
host: baseUrl,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import Footer from "@/components/shared/layout/footer/Footer";
|
||||
import ServicesScrollProgressButton from "@/components/pages/services/ServicesScrollProgressButton";
|
||||
import ServicesInitAnimations from "@/components/pages/services/ServicesInitAnimations";
|
||||
import { serviceService, Service } from "@/lib/api/serviceService";
|
||||
import { generateServiceMetadata } from "@/lib/seo/metadata";
|
||||
import { ServiceSchema, BreadcrumbSchema } from "@/components/shared/seo/StructuredData";
|
||||
|
||||
interface ServicePageProps {
|
||||
params: Promise<{
|
||||
@@ -30,21 +32,13 @@ export async function generateStaticParams() {
|
||||
}
|
||||
}
|
||||
|
||||
// Generate metadata for each service page
|
||||
// Generate enhanced metadata for each service page
|
||||
export async function generateMetadata({ params }: ServicePageProps) {
|
||||
try {
|
||||
const { slug } = await params;
|
||||
const service = await serviceService.getServiceBySlug(slug);
|
||||
|
||||
return {
|
||||
title: `${service.title} - GNX Services`,
|
||||
description: service.description,
|
||||
openGraph: {
|
||||
title: service.title,
|
||||
description: service.description,
|
||||
images: service.image_url ? [service.image_url] : [],
|
||||
},
|
||||
};
|
||||
return generateServiceMetadata(service);
|
||||
} catch (error) {
|
||||
return {
|
||||
title: 'Service Not Found - GNX',
|
||||
@@ -64,8 +58,19 @@ const ServicePage = async ({ params }: ServicePageProps) => {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Breadcrumb data for structured data
|
||||
const breadcrumbItems = [
|
||||
{ name: 'Home', url: '/' },
|
||||
{ name: 'Services', url: '/services' },
|
||||
{ name: service.title, url: `/services/${service.slug}` },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="enterprise-app">
|
||||
{/* SEO Structured Data */}
|
||||
<ServiceSchema service={service} />
|
||||
<BreadcrumbSchema items={breadcrumbItems} />
|
||||
|
||||
<Header />
|
||||
<main className="enterprise-main">
|
||||
<ServiceDetailsBanner service={service} />
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
import { Metadata } from 'next';
|
||||
import Header from "@/components/shared/layout/header/Header";
|
||||
import ServicesBanner from "@/components/pages/services/ServicesBanner";
|
||||
import ServiceMain from "@/components/pages/services/ServiceMain";
|
||||
import Footer from "@/components/shared/layout/footer/Footer";
|
||||
import ServicesScrollProgressButton from "@/components/pages/services/ServicesScrollProgressButton";
|
||||
import ServicesInitAnimations from "@/components/pages/services/ServicesInitAnimations";
|
||||
import { generateMetadata as createMetadata } from "@/lib/seo/metadata";
|
||||
|
||||
export const metadata: Metadata = createMetadata({
|
||||
title: "Our Services - Enterprise Software Development",
|
||||
description: "Explore our comprehensive range of enterprise software development services including custom software, data replication, incident management, AI business intelligence, backend & frontend engineering, and systems integration.",
|
||||
keywords: [
|
||||
"Software Development Services",
|
||||
"Custom Software Development",
|
||||
"Data Replication",
|
||||
"Incident Management SaaS",
|
||||
"AI Business Intelligence",
|
||||
"Backend Engineering Services",
|
||||
"Frontend Development",
|
||||
"Systems Integration Services",
|
||||
],
|
||||
url: "/services",
|
||||
});
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
|
||||
137
gnx-react/app/sitemap.ts
Normal file
137
gnx-react/app/sitemap.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://gnxsoft.com';
|
||||
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api';
|
||||
|
||||
// Static pages
|
||||
const staticPages: MetadataRoute.Sitemap = [
|
||||
{
|
||||
url: baseUrl,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'daily',
|
||||
priority: 1.0,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/about-us`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/services`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'weekly',
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/case-study`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'weekly',
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/insights`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'daily',
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/career`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'weekly',
|
||||
priority: 0.7,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/contact-us`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/support-center`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.7,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/policy`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'yearly',
|
||||
priority: 0.5,
|
||||
},
|
||||
];
|
||||
|
||||
try {
|
||||
// Fetch dynamic services
|
||||
const servicesResponse = await fetch(`${apiUrl}/services/`, {
|
||||
next: { revalidate: 3600 }, // Revalidate every hour
|
||||
});
|
||||
|
||||
let servicePages: MetadataRoute.Sitemap = [];
|
||||
if (servicesResponse.ok) {
|
||||
const services = await servicesResponse.json();
|
||||
servicePages = services.map((service: any) => ({
|
||||
url: `${baseUrl}/services/${service.slug}`,
|
||||
lastModified: new Date(service.updated_at || service.created_at),
|
||||
changeFrequency: 'weekly' as const,
|
||||
priority: service.featured ? 0.9 : 0.7,
|
||||
}));
|
||||
}
|
||||
|
||||
// Fetch dynamic blog posts
|
||||
const blogResponse = await fetch(`${apiUrl}/blog/`, {
|
||||
next: { revalidate: 3600 },
|
||||
});
|
||||
|
||||
let blogPages: MetadataRoute.Sitemap = [];
|
||||
if (blogResponse.ok) {
|
||||
const posts = await blogResponse.json();
|
||||
blogPages = posts.map((post: any) => ({
|
||||
url: `${baseUrl}/insights/${post.slug}`,
|
||||
lastModified: new Date(post.updated_at || post.published_at),
|
||||
changeFrequency: 'weekly' as const,
|
||||
priority: 0.7,
|
||||
}));
|
||||
}
|
||||
|
||||
// Fetch dynamic case studies
|
||||
const caseStudiesResponse = await fetch(`${apiUrl}/case-studies/`, {
|
||||
next: { revalidate: 3600 },
|
||||
});
|
||||
|
||||
let caseStudyPages: MetadataRoute.Sitemap = [];
|
||||
if (caseStudiesResponse.ok) {
|
||||
const caseStudies = await caseStudiesResponse.json();
|
||||
caseStudyPages = caseStudies.map((study: any) => ({
|
||||
url: `${baseUrl}/case-study/${study.slug}`,
|
||||
lastModified: new Date(study.updated_at || study.created_at),
|
||||
changeFrequency: 'monthly' as const,
|
||||
priority: 0.7,
|
||||
}));
|
||||
}
|
||||
|
||||
// Fetch dynamic career postings
|
||||
const careerResponse = await fetch(`${apiUrl}/career/positions/`, {
|
||||
next: { revalidate: 3600 },
|
||||
});
|
||||
|
||||
let careerPages: MetadataRoute.Sitemap = [];
|
||||
if (careerResponse.ok) {
|
||||
const positions = await careerResponse.json();
|
||||
careerPages = positions.map((position: any) => ({
|
||||
url: `${baseUrl}/career/${position.slug}`,
|
||||
lastModified: new Date(position.updated_at || position.created_at),
|
||||
changeFrequency: 'weekly' as const,
|
||||
priority: 0.6,
|
||||
}));
|
||||
}
|
||||
|
||||
return [...staticPages, ...servicePages, ...blogPages, ...caseStudyPages, ...careerPages];
|
||||
} catch (error) {
|
||||
console.error('Error generating sitemap:', error);
|
||||
// Return at least static pages if API fails
|
||||
return staticPages;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
105
gnx-react/backend/gnx/middleware/ip_whitelist.py
Normal file
105
gnx-react/backend/gnx/middleware/ip_whitelist.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
IP Whitelist Middleware
|
||||
Only allows requests from internal network
|
||||
Backend is NOT accessible from public internet
|
||||
"""
|
||||
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.conf import settings
|
||||
import ipaddress
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('django.security')
|
||||
|
||||
|
||||
class IPWhitelistMiddleware:
|
||||
"""
|
||||
Enterprise Security: Only allow requests from whitelisted IPs (internal network)
|
||||
This ensures backend API is NEVER directly accessible from internet
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
# Define allowed internal network ranges
|
||||
self.allowed_networks = [
|
||||
ipaddress.ip_network('127.0.0.0/8'), # localhost
|
||||
ipaddress.ip_network('10.0.0.0/8'), # Private Class A
|
||||
ipaddress.ip_network('172.16.0.0/12'), # Private Class B
|
||||
ipaddress.ip_network('192.168.0.0/16'), # Private Class C
|
||||
ipaddress.ip_network('::1/128'), # IPv6 localhost
|
||||
ipaddress.ip_network('fe80::/10'), # IPv6 link-local
|
||||
]
|
||||
|
||||
# Add custom allowed IPs from settings if any
|
||||
custom_allowed = getattr(settings, 'CUSTOM_ALLOWED_IPS', [])
|
||||
for ip in custom_allowed:
|
||||
try:
|
||||
self.allowed_networks.append(ipaddress.ip_network(ip))
|
||||
except ValueError:
|
||||
logger.warning(f"Invalid IP network in CUSTOM_ALLOWED_IPS: {ip}")
|
||||
|
||||
def __call__(self, request):
|
||||
# Get client IP address
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
# Get the first IP in the chain (original client)
|
||||
ip = x_forwarded_for.split(',')[0].strip()
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
|
||||
# In development, allow all
|
||||
if settings.DEBUG:
|
||||
return self.get_response(request)
|
||||
|
||||
try:
|
||||
client_ip = ipaddress.ip_address(ip)
|
||||
|
||||
# Check if IP is in allowed networks
|
||||
is_allowed = any(
|
||||
client_ip in network
|
||||
for network in self.allowed_networks
|
||||
)
|
||||
|
||||
if not is_allowed:
|
||||
logger.warning(
|
||||
f"Blocked external access attempt from IP: {ip} "
|
||||
f"to path: {request.path}"
|
||||
)
|
||||
return HttpResponseForbidden(
|
||||
"Access Denied: This API is for internal use only. "
|
||||
"Backend is not accessible from public internet."
|
||||
)
|
||||
|
||||
except ValueError:
|
||||
logger.error(f"Invalid IP address format: {ip}")
|
||||
return HttpResponseForbidden("Access Denied: Invalid IP address")
|
||||
|
||||
# IP is whitelisted, process request
|
||||
return self.get_response(request)
|
||||
|
||||
|
||||
class SecurityHeadersMiddleware:
|
||||
"""
|
||||
Additional security headers for defense in depth
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
|
||||
# Additional security headers
|
||||
response['X-Content-Type-Options'] = 'nosniff'
|
||||
response['X-Frame-Options'] = 'DENY'
|
||||
response['X-XSS-Protection'] = '1; mode=block'
|
||||
response['Referrer-Policy'] = 'strict-origin-when-cross-origin'
|
||||
response['Permissions-Policy'] = 'geolocation=(), microphone=(), camera=()'
|
||||
|
||||
# Remove server identification
|
||||
if 'Server' in response:
|
||||
del response['Server']
|
||||
|
||||
return response
|
||||
|
||||
@@ -60,6 +60,7 @@ INSTALLED_APPS = [
|
||||
MIDDLEWARE = [
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'gnx.middleware.ip_whitelist.IPWhitelistMiddleware', # Production: Block external access
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
@@ -109,6 +110,9 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
'OPTIONS': {
|
||||
'min_length': 12 if not DEBUG else 8, # Enterprise-grade in production
|
||||
}
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
@@ -118,6 +122,44 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
},
|
||||
]
|
||||
|
||||
# ============================================================================
|
||||
# PRODUCTION SECURITY SETTINGS
|
||||
# Backend accessible ONLY from internal network in production
|
||||
# ============================================================================
|
||||
|
||||
# Security Headers
|
||||
SECURE_SSL_REDIRECT = config('SECURE_SSL_REDIRECT', default=False, cast=bool)
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
SECURE_HSTS_SECONDS = config('SECURE_HSTS_SECONDS', default=31536000 if not DEBUG else 0, cast=int)
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = config('SECURE_HSTS_INCLUDE_SUBDOMAINS', default=not DEBUG, cast=bool)
|
||||
SECURE_HSTS_PRELOAD = config('SECURE_HSTS_PRELOAD', default=not DEBUG, cast=bool)
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
X_FRAME_OPTIONS = 'DENY'
|
||||
SECURE_REFERRER_POLICY = 'strict-origin-when-cross-origin'
|
||||
|
||||
# Session Security
|
||||
SESSION_COOKIE_SECURE = config('SESSION_COOKIE_SECURE', default=not DEBUG, cast=bool)
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
SESSION_COOKIE_SAMESITE = 'Strict'
|
||||
SESSION_COOKIE_AGE = 1209600 # 2 weeks
|
||||
|
||||
# CSRF Security
|
||||
CSRF_COOKIE_SECURE = config('CSRF_COOKIE_SECURE', default=not DEBUG, cast=bool)
|
||||
CSRF_COOKIE_HTTPONLY = True
|
||||
CSRF_COOKIE_SAMESITE = 'Strict'
|
||||
CSRF_TRUSTED_ORIGINS = config(
|
||||
'CSRF_TRUSTED_ORIGINS',
|
||||
default='https://gnxsoft.com',
|
||||
cast=lambda v: [s.strip() for s in v.split(',')]
|
||||
)
|
||||
|
||||
# Internal IPs - Backend should only be accessed from these
|
||||
INTERNAL_IPS = ['127.0.0.1', '::1']
|
||||
|
||||
# Custom allowed IPs for IP whitelist middleware (comma-separated)
|
||||
CUSTOM_ALLOWED_IPS = config('CUSTOM_ALLOWED_IPS', default='', cast=lambda v: [s.strip() for s in v.split(',') if s.strip()])
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||
@@ -170,7 +212,20 @@ REST_FRAMEWORK = {
|
||||
'DEFAULT_RENDERER_CLASSES': [
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
'rest_framework.renderers.BrowsableAPIRenderer',
|
||||
] if DEBUG else [
|
||||
'rest_framework.renderers.JSONRenderer', # Production: JSON only, no browsable API
|
||||
],
|
||||
# Rate Limiting (Production)
|
||||
'DEFAULT_THROTTLE_CLASSES': [
|
||||
'rest_framework.throttling.AnonRateThrottle',
|
||||
'rest_framework.throttling.UserRateThrottle',
|
||||
] if not DEBUG else [],
|
||||
'DEFAULT_THROTTLE_RATES': {
|
||||
'anon': '100/hour', # Anonymous users
|
||||
'user': '1000/hour', # Authenticated users
|
||||
'burst': '60/min', # Short-term burst
|
||||
'sustained': '1000/day', # Long-term sustained
|
||||
},
|
||||
}
|
||||
|
||||
# CORS Configuration
|
||||
@@ -181,6 +236,11 @@ CORS_ALLOWED_ORIGINS = [
|
||||
"http://127.0.0.1:3001",
|
||||
]
|
||||
|
||||
# Add production origins if configured
|
||||
PRODUCTION_ORIGINS = config('PRODUCTION_ORIGINS', default='', cast=lambda v: [s.strip() for s in v.split(',') if s.strip()])
|
||||
if PRODUCTION_ORIGINS:
|
||||
CORS_ALLOWED_ORIGINS.extend(PRODUCTION_ORIGINS)
|
||||
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
|
||||
CORS_ALLOW_ALL_ORIGINS = DEBUG # Only allow all origins in development
|
||||
@@ -229,8 +289,18 @@ LOGGING = {
|
||||
'handlers': {
|
||||
'file': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.FileHandler',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': BASE_DIR / 'logs' / 'django.log',
|
||||
'maxBytes': 1024 * 1024 * 15, # 15MB
|
||||
'backupCount': 10,
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'security_file': {
|
||||
'level': 'WARNING',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': BASE_DIR / 'logs' / 'security.log',
|
||||
'maxBytes': 1024 * 1024 * 15, # 15MB
|
||||
'backupCount': 10,
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'console': {
|
||||
@@ -249,6 +319,11 @@ LOGGING = {
|
||||
'level': 'INFO',
|
||||
'propagate': False,
|
||||
},
|
||||
'django.security': {
|
||||
'handlers': ['security_file', 'console'],
|
||||
'level': 'WARNING',
|
||||
'propagate': False,
|
||||
},
|
||||
'contact': {
|
||||
'handlers': ['file', 'console'],
|
||||
'level': 'DEBUG',
|
||||
@@ -256,3 +331,17 @@ LOGGING = {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# PRODUCTION NOTES
|
||||
# ============================================================================
|
||||
# In production:
|
||||
# 1. Set DEBUG=False in environment
|
||||
# 2. Backend runs on 127.0.0.1:8000 (internal only)
|
||||
# 3. Firewall blocks external access to port 8000
|
||||
# 4. Frontend proxies API calls through Nginx
|
||||
# 5. IP Whitelist Middleware blocks non-internal IPs
|
||||
# 6. Rate limiting protects against abuse
|
||||
# 7. Browsable API disabled (JSON only)
|
||||
# ============================================================================
|
||||
|
||||
|
||||
61
gnx-react/backend/gnx/throttling.py
Normal file
61
gnx-react/backend/gnx/throttling.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
Rate Limiting for Enterprise Security
|
||||
Prevents abuse and DDoS attacks
|
||||
"""
|
||||
|
||||
from rest_framework.throttling import SimpleRateThrottle
|
||||
|
||||
|
||||
class BurstRateThrottle(SimpleRateThrottle):
|
||||
"""
|
||||
Short-term burst protection
|
||||
"""
|
||||
scope = 'burst'
|
||||
|
||||
def get_cache_key(self, request, view):
|
||||
if request.user.is_authenticated:
|
||||
ident = request.user.pk
|
||||
else:
|
||||
ident = self.get_ident(request)
|
||||
|
||||
return self.cache_format % {
|
||||
'scope': self.scope,
|
||||
'ident': ident
|
||||
}
|
||||
|
||||
|
||||
class SustainedRateThrottle(SimpleRateThrottle):
|
||||
"""
|
||||
Long-term sustained rate limiting
|
||||
"""
|
||||
scope = 'sustained'
|
||||
|
||||
def get_cache_key(self, request, view):
|
||||
if request.user.is_authenticated:
|
||||
ident = request.user.pk
|
||||
else:
|
||||
ident = self.get_ident(request)
|
||||
|
||||
return self.cache_format % {
|
||||
'scope': self.scope,
|
||||
'ident': ident
|
||||
}
|
||||
|
||||
|
||||
# Add to settings_production.py:
|
||||
"""
|
||||
REST_FRAMEWORK = {
|
||||
...
|
||||
'DEFAULT_THROTTLE_CLASSES': [
|
||||
'gnx.throttling.BurstRateThrottle',
|
||||
'gnx.throttling.SustainedRateThrottle',
|
||||
],
|
||||
'DEFAULT_THROTTLE_RATES': {
|
||||
'burst': '60/min',
|
||||
'sustained': '1000/day',
|
||||
'anon': '100/hour',
|
||||
'user': '1000/hour',
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
0
gnx-react/backend/logs/security.log
Normal file
0
gnx-react/backend/logs/security.log
Normal file
@@ -6,8 +6,8 @@ SECRET_KEY=your-super-secret-production-key-here
|
||||
DEBUG=False
|
||||
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com,your-server-ip
|
||||
|
||||
# Database (Production)
|
||||
DATABASE_URL=postgresql://username:password@localhost:5432/gnx_production
|
||||
# Database - Using SQLite (default)
|
||||
# SQLite is configured in settings.py - no DATABASE_URL needed
|
||||
|
||||
# Email Configuration (Production)
|
||||
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
|
||||
|
||||
Binary file not shown.
@@ -17,30 +17,10 @@ class Command(BaseCommand):
|
||||
|
||||
# Create fresh categories
|
||||
categories = {
|
||||
'web-development': {
|
||||
'name': 'Web Development',
|
||||
'description': 'Custom web applications and modern websites',
|
||||
'enterprise-content': {
|
||||
'name': 'Enterprise Content',
|
||||
'description': 'Enterprise-grade solutions for modern businesses',
|
||||
'display_order': 1
|
||||
},
|
||||
'mobile-development': {
|
||||
'name': 'Mobile Development',
|
||||
'description': 'Native and cross-platform mobile applications',
|
||||
'display_order': 2
|
||||
},
|
||||
'api-development': {
|
||||
'name': 'API Development',
|
||||
'description': 'RESTful APIs and microservices architecture',
|
||||
'display_order': 3
|
||||
},
|
||||
'cloud-services': {
|
||||
'name': 'Cloud Services',
|
||||
'description': 'Cloud migration and infrastructure management',
|
||||
'display_order': 4
|
||||
},
|
||||
'ai-ml': {
|
||||
'name': 'AI & Machine Learning',
|
||||
'description': 'Artificial intelligence and machine learning solutions',
|
||||
'display_order': 5
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,141 +41,164 @@ class Command(BaseCommand):
|
||||
# Create fresh services with detailed data
|
||||
services_data = [
|
||||
{
|
||||
'title': 'Enterprise Web Application',
|
||||
'description': 'Build powerful, scalable web applications tailored to your business needs. Our expert developers use modern frameworks and technologies to create solutions that drive growth and efficiency.',
|
||||
'short_description': 'Custom enterprise web applications with modern architecture and scalable infrastructure.',
|
||||
'slug': 'enterprise-web-application',
|
||||
'title': 'Custom Software Development',
|
||||
'description': 'We design and build tailored digital solutions that align precisely with your business goals — delivering reliable, scalable, and future-ready applications that drive measurable value.',
|
||||
'short_description': 'Tailored digital solutions aligned with your business goals.',
|
||||
'slug': 'custom-software-development',
|
||||
'icon': 'code',
|
||||
'price': '25000.00',
|
||||
'category_slug': 'web-development',
|
||||
'duration': '8-12 weeks',
|
||||
'deliverables': 'Complete Web Application, Admin Dashboard, User Authentication System, Database Design, API Integration, Responsive Design, Security Implementation, Testing Suite, Deployment Setup, Documentation, Training Materials, 3 Months Support',
|
||||
'technologies': 'React, Next.js, TypeScript, Node.js, PostgreSQL, Redis, AWS, Docker, Kubernetes, Jest, Cypress',
|
||||
'process_steps': 'Discovery & Requirements, UI/UX Design, Architecture Planning, Backend Development, Frontend Development, API Development, Database Setup, Security Implementation, Testing & QA, Deployment, Training, Launch Support',
|
||||
'price': '50000.00',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '12-20 weeks',
|
||||
'deliverables': 'Custom Application, System Architecture, Database Design, API Development, User Interface, Testing Suite, Deployment Setup, Documentation, Training, 6 Months Support',
|
||||
'technologies': 'React, Next.js, TypeScript, Node.js, Python, PostgreSQL, MongoDB, Redis, AWS, Docker, Kubernetes',
|
||||
'process_steps': 'Requirements Analysis, Solution Design, Architecture Planning, Development, Testing, Deployment, Training, Launch Support',
|
||||
'featured': True,
|
||||
'display_order': 1,
|
||||
'features': [
|
||||
{'title': 'Scalable Architecture', 'description': 'Built with microservices and cloud-native architecture', 'icon': 'cloud'},
|
||||
{'title': 'Advanced Security', 'description': 'Enterprise-grade security with authentication and authorization', 'icon': 'shield'},
|
||||
{'title': 'Real-time Features', 'description': 'WebSocket integration for real-time updates and notifications', 'icon': 'bolt'},
|
||||
{'title': 'Mobile Responsive', 'description': 'Fully responsive design that works on all devices', 'icon': 'mobile'},
|
||||
{'title': 'Performance Optimized', 'description': 'Optimized for speed and performance with caching strategies', 'icon': 'gauge'},
|
||||
{'title': 'Analytics Integration', 'description': 'Built-in analytics and reporting capabilities', 'icon': 'chart-bar'}
|
||||
{'title': 'Custom Solutions', 'description': 'Tailored applications built for your specific needs', 'icon': 'cogs'},
|
||||
{'title': 'Scalable Architecture', 'description': 'Built to grow with your business', 'icon': 'expand'},
|
||||
{'title': 'Future-Ready', 'description': 'Modern tech stack for long-term sustainability', 'icon': 'rocket'},
|
||||
{'title': 'Business Alignment', 'description': 'Solutions that drive measurable business value', 'icon': 'chart-line'},
|
||||
{'title': 'Reliable', 'description': 'Enterprise-grade reliability and performance', 'icon': 'shield'},
|
||||
{'title': 'Full Support', 'description': 'Comprehensive training and ongoing support', 'icon': 'headset'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'Cross-Platform Mobile App',
|
||||
'description': 'Create stunning mobile applications for iOS and Android platforms. We deliver native and cross-platform solutions that provide exceptional user experiences and drive engagement.',
|
||||
'short_description': 'Native and cross-platform mobile applications with modern UI/UX design.',
|
||||
'slug': 'cross-platform-mobile-app',
|
||||
'icon': 'mobile',
|
||||
'title': 'Data Replication',
|
||||
'description': 'We ensure secure, real-time synchronization across your databases and systems — maintaining data accuracy, consistency, and availability for mission-critical operations.',
|
||||
'short_description': 'Secure real-time data synchronization across systems.',
|
||||
'slug': 'data-replication',
|
||||
'icon': 'sync',
|
||||
'price': '35000.00',
|
||||
'category_slug': 'mobile-development',
|
||||
'duration': '12-16 weeks',
|
||||
'deliverables': 'iOS Mobile App, Android Mobile App, Admin Panel, Backend API, Push Notifications, Offline Support, App Store Submission, Google Play Submission, User Documentation, Admin Guide, Testing Suite, 6 Months Support',
|
||||
'technologies': 'React Native, TypeScript, Node.js, MongoDB, Firebase, AWS, App Store Connect, Google Play Console, Jest, Detox',
|
||||
'process_steps': 'Market Research, UI/UX Design, Prototyping, Backend Development, Mobile App Development, API Integration, Testing, App Store Optimization, Submission Process, Launch Strategy, Post-launch Support',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '8-12 weeks',
|
||||
'deliverables': 'Replication System, Data Pipeline, Monitoring Dashboard, Conflict Resolution, Backup Strategy, Security Configuration, Performance Tuning, Documentation, 6 Months Support',
|
||||
'technologies': 'PostgreSQL, MySQL, MongoDB, Redis, Apache Kafka, AWS DMS, Azure Data Sync, Change Data Capture, ETL Tools',
|
||||
'process_steps': 'Data Assessment, Architecture Design, Pipeline Setup, Testing, Security Implementation, Monitoring Setup, Optimization, Documentation, Go-live',
|
||||
'featured': True,
|
||||
'display_order': 2,
|
||||
'features': [
|
||||
{'title': 'Cross-Platform', 'description': 'Single codebase for both iOS and Android platforms', 'icon': 'mobile'},
|
||||
{'title': 'Native Performance', 'description': 'Optimized performance using native components and modules', 'icon': 'gauge'},
|
||||
{'title': 'Offline Support', 'description': 'Full offline functionality with data synchronization', 'icon': 'wifi'},
|
||||
{'title': 'Push Notifications', 'description': 'Real-time push notifications for user engagement', 'icon': 'bell'},
|
||||
{'title': 'App Store Ready', 'description': 'Complete app store submission and approval process', 'icon': 'store'},
|
||||
{'title': 'Analytics Dashboard', 'description': 'Comprehensive analytics and user behavior tracking', 'icon': 'chart-line'}
|
||||
{'title': 'Real-Time Sync', 'description': 'Instant data synchronization across systems', 'icon': 'bolt'},
|
||||
{'title': 'Data Accuracy', 'description': 'Ensures consistency and accuracy across databases', 'icon': 'check-circle'},
|
||||
{'title': 'High Availability', 'description': 'Mission-critical data always available', 'icon': 'server'},
|
||||
{'title': 'Secure Transfer', 'description': 'Encrypted data transmission and storage', 'icon': 'lock'},
|
||||
{'title': 'Conflict Resolution', 'description': 'Automated handling of data conflicts', 'icon': 'cogs'},
|
||||
{'title': 'Performance', 'description': 'Optimized for minimal impact on operations', 'icon': 'gauge'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'RESTful API Development',
|
||||
'description': 'Build robust, scalable APIs that power your applications and enable seamless integration with third-party services. Our APIs are designed for performance, security, and maintainability.',
|
||||
'short_description': 'Enterprise-grade RESTful APIs with comprehensive documentation and security.',
|
||||
'slug': 'restful-api-development',
|
||||
'icon': 'api',
|
||||
'price': '15000.00',
|
||||
'category_slug': 'api-development',
|
||||
'duration': '4-6 weeks',
|
||||
'deliverables': 'RESTful API, API Documentation, Authentication System, Rate Limiting, API Testing Suite, Postman Collection, SDK Development, Integration Examples, Performance Monitoring, Security Audit, Deployment Guide, 3 Months Support',
|
||||
'technologies': 'Node.js, Express, TypeScript, PostgreSQL, Redis, JWT, Swagger, Postman, Jest, AWS API Gateway, Docker',
|
||||
'process_steps': 'API Planning, Database Design, Authentication Setup, Endpoint Development, Documentation, Testing, Security Review, Performance Optimization, Deployment, Integration Testing, Monitoring Setup',
|
||||
'featured': False,
|
||||
'title': 'Incident Management SaaS',
|
||||
'description': 'We provide intelligent, cloud-based incident management tools that empower teams to detect, respond, and resolve issues faster — minimizing downtime and protecting customer trust.',
|
||||
'short_description': 'Cloud-based incident management for faster issue resolution.',
|
||||
'slug': 'incident-management-saas',
|
||||
'icon': 'bell',
|
||||
'price': '45000.00',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '16-20 weeks',
|
||||
'deliverables': 'SaaS Platform, Incident Dashboard, Alert System, Integration APIs, Mobile App, Reporting Tools, Analytics, Automation Workflows, Documentation, 12 Months Support',
|
||||
'technologies': 'React, Node.js, TypeScript, PostgreSQL, Redis, WebSockets, AWS, Kubernetes, PagerDuty API, Slack Integration, Twilio',
|
||||
'process_steps': 'Requirements Analysis, Platform Design, Core Development, Integration Development, Testing, Security Audit, Deployment, Training, Launch',
|
||||
'featured': True,
|
||||
'display_order': 3,
|
||||
'features': [
|
||||
{'title': 'RESTful Design', 'description': 'Clean, intuitive API design following REST principles', 'icon': 'code'},
|
||||
{'title': 'Comprehensive Documentation', 'description': 'Interactive API documentation with examples', 'icon': 'book'},
|
||||
{'title': 'Authentication & Security', 'description': 'JWT-based authentication with rate limiting', 'icon': 'shield'},
|
||||
{'title': 'Performance Optimized', 'description': 'Caching and optimization for high performance', 'icon': 'gauge'},
|
||||
{'title': 'SDK Support', 'description': 'Client SDKs for easy integration', 'icon': 'puzzle-piece'},
|
||||
{'title': 'Monitoring & Analytics', 'description': 'Built-in monitoring and usage analytics', 'icon': 'chart-bar'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'Cloud Migration & DevOps',
|
||||
'description': 'Migrate your existing infrastructure to the cloud with minimal downtime. We help you leverage cloud technologies for improved scalability, security, and cost efficiency.',
|
||||
'short_description': 'Complete cloud migration with DevOps automation and monitoring.',
|
||||
'slug': 'cloud-migration-devops',
|
||||
'icon': 'cloud',
|
||||
'price': '45000.00',
|
||||
'category_slug': 'cloud-services',
|
||||
'duration': '16-20 weeks',
|
||||
'deliverables': 'Cloud Infrastructure Setup, Application Migration, CI/CD Pipeline, Monitoring Setup, Security Configuration, Backup Strategy, Disaster Recovery Plan, Cost Optimization, Performance Tuning, Documentation, Training, 6 Months Support',
|
||||
'technologies': 'AWS, Azure, Google Cloud, Docker, Kubernetes, Terraform, Jenkins, GitLab CI, Prometheus, Grafana, ELK Stack',
|
||||
'process_steps': 'Infrastructure Assessment, Migration Planning, Security Audit, Infrastructure Setup, Application Migration, CI/CD Implementation, Monitoring Setup, Testing, Go-live, Optimization, Documentation, Training',
|
||||
'featured': True,
|
||||
'display_order': 4,
|
||||
'features': [
|
||||
{'title': 'Zero Downtime Migration', 'description': 'Seamless migration with minimal service interruption', 'icon': 'clock'},
|
||||
{'title': 'Cost Optimization', 'description': 'Optimized cloud resources for maximum cost efficiency', 'icon': 'dollar-sign'},
|
||||
{'title': 'Security First', 'description': 'Enhanced security with cloud-native security features', 'icon': 'shield'},
|
||||
{'title': 'Automated DevOps', 'description': 'Complete CI/CD pipeline with automated deployments', 'icon': 'cogs'},
|
||||
{'title': 'Monitoring & Alerting', 'description': 'Comprehensive monitoring and alerting system', 'icon': 'bell'},
|
||||
{'title': 'Scalability', 'description': 'Auto-scaling infrastructure for handling traffic spikes', 'icon': 'expand'}
|
||||
{'title': 'Intelligent Detection', 'description': 'AI-powered incident detection and alerting', 'icon': 'brain'},
|
||||
{'title': 'Rapid Response', 'description': 'Tools to respond and resolve issues faster', 'icon': 'bolt'},
|
||||
{'title': 'Minimize Downtime', 'description': 'Reduce system downtime and service disruption', 'icon': 'clock'},
|
||||
{'title': 'Team Collaboration', 'description': 'Empower teams with collaborative tools', 'icon': 'users'},
|
||||
{'title': 'Cloud-Based', 'description': 'Accessible anywhere, anytime', 'icon': 'cloud'},
|
||||
{'title': 'Customer Trust', 'description': 'Protect and maintain customer confidence', 'icon': 'shield'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'AI-Powered Business Intelligence',
|
||||
'description': 'Transform your business data into actionable insights with AI-powered analytics and machine learning solutions. Make data-driven decisions with advanced predictive analytics.',
|
||||
'short_description': 'AI-powered business intelligence and predictive analytics platform.',
|
||||
'description': 'We transform enterprise data into actionable insights with advanced analytics and AI — enabling smarter decisions, performance optimization, and data-driven innovation.',
|
||||
'short_description': 'Transform data into insights with AI and advanced analytics.',
|
||||
'slug': 'ai-powered-business-intelligence',
|
||||
'icon': 'brain',
|
||||
'price': '55000.00',
|
||||
'category_slug': 'ai-ml',
|
||||
'price': '60000.00',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '20-24 weeks',
|
||||
'deliverables': 'AI Analytics Platform, Machine Learning Models, Data Pipeline, Dashboard Development, Predictive Analytics, Report Generation, API Integration, Data Visualization, Model Training, Performance Monitoring, Documentation, 12 Months Support',
|
||||
'technologies': 'Python, TensorFlow, PyTorch, Pandas, NumPy, Scikit-learn, React, D3.js, PostgreSQL, Redis, AWS SageMaker, Docker',
|
||||
'process_steps': 'Data Analysis, Model Selection, Data Pipeline Development, Model Training, Dashboard Development, API Development, Testing, Deployment, Performance Monitoring, Optimization, Documentation, Training',
|
||||
'deliverables': 'BI Platform, Machine Learning Models, Data Pipeline, Interactive Dashboards, Predictive Analytics, Report Generation, API Integration, Model Training, Documentation, 12 Months Support',
|
||||
'technologies': 'Python, TensorFlow, PyTorch, Pandas, Scikit-learn, React, D3.js, Tableau, PostgreSQL, Snowflake, AWS SageMaker, Apache Spark',
|
||||
'process_steps': 'Data Analysis, Model Development, Dashboard Design, Pipeline Development, Training, Integration, Testing, Optimization, Deployment, Training',
|
||||
'featured': True,
|
||||
'display_order': 5,
|
||||
'display_order': 4,
|
||||
'features': [
|
||||
{'title': 'Predictive Analytics', 'description': 'Advanced ML models for business forecasting', 'icon': 'chart-line'},
|
||||
{'title': 'Real-time Insights', 'description': 'Live data processing and real-time analytics', 'icon': 'bolt'},
|
||||
{'title': 'Interactive Dashboards', 'description': 'Beautiful, interactive data visualization', 'icon': 'chart-bar'},
|
||||
{'title': 'Automated Reports', 'description': 'Automated report generation and distribution', 'icon': 'file-alt'},
|
||||
{'title': 'Data Integration', 'description': 'Seamless integration with existing data sources', 'icon': 'plug'},
|
||||
{'title': 'Scalable Architecture', 'description': 'Cloud-native architecture for handling big data', 'icon': 'cloud'}
|
||||
{'title': 'Actionable Insights', 'description': 'Transform raw data into meaningful insights', 'icon': 'lightbulb'},
|
||||
{'title': 'Advanced Analytics', 'description': 'AI-powered analytics and predictions', 'icon': 'chart-line'},
|
||||
{'title': 'Smart Decisions', 'description': 'Enable data-driven decision making', 'icon': 'brain'},
|
||||
{'title': 'Performance Optimization', 'description': 'Identify and optimize business performance', 'icon': 'gauge'},
|
||||
{'title': 'Data-Driven Innovation', 'description': 'Unlock new opportunities through data', 'icon': 'rocket'},
|
||||
{'title': 'Enterprise Scale', 'description': 'Built to handle enterprise data volumes', 'icon': 'database'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'E-commerce Platform',
|
||||
'description': 'Build a complete e-commerce solution with modern features, secure payment processing, and advanced analytics. Create an online store that converts visitors into customers.',
|
||||
'short_description': 'Complete e-commerce platform with payment processing and analytics.',
|
||||
'slug': 'ecommerce-platform',
|
||||
'icon': 'shopping-cart',
|
||||
'price': '30000.00',
|
||||
'category_slug': 'web-development',
|
||||
'title': 'Backend Engineering',
|
||||
'description': 'We architect and optimize high-performance backend systems — ensuring your applications run securely, efficiently, and scale effortlessly as your business grows.',
|
||||
'short_description': 'High-performance backend systems that scale effortlessly.',
|
||||
'slug': 'backend-engineering',
|
||||
'icon': 'server',
|
||||
'price': '40000.00',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '10-16 weeks',
|
||||
'deliverables': 'Backend Architecture, API Development, Database Design, Authentication System, Caching Strategy, Performance Optimization, Security Implementation, Testing, Documentation, 6 Months Support',
|
||||
'technologies': 'Node.js, Python, Django, FastAPI, PostgreSQL, MongoDB, Redis, RabbitMQ, GraphQL, REST, Docker, Kubernetes, AWS',
|
||||
'process_steps': 'Architecture Design, Database Modeling, API Development, Security Implementation, Optimization, Testing, Deployment, Monitoring Setup, Documentation',
|
||||
'featured': True,
|
||||
'display_order': 5,
|
||||
'features': [
|
||||
{'title': 'High Performance', 'description': 'Optimized for speed and efficiency', 'icon': 'gauge'},
|
||||
{'title': 'Secure', 'description': 'Enterprise-grade security implementation', 'icon': 'shield'},
|
||||
{'title': 'Scalable', 'description': 'Scales effortlessly with business growth', 'icon': 'expand'},
|
||||
{'title': 'Efficient', 'description': 'Resource-optimized for cost efficiency', 'icon': 'cogs'},
|
||||
{'title': 'Reliable', 'description': 'Built for uptime and reliability', 'icon': 'check-circle'},
|
||||
{'title': 'Modern Architecture', 'description': 'Microservices and cloud-native design', 'icon': 'cloud'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'Frontend Engineering',
|
||||
'description': 'We craft responsive, accessible, and engaging user interfaces — blending performance with design to deliver exceptional digital experiences across devices and platforms.',
|
||||
'short_description': 'Responsive, engaging UIs for exceptional digital experiences.',
|
||||
'slug': 'frontend-engineering',
|
||||
'icon': 'palette',
|
||||
'price': '35000.00',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '10-14 weeks',
|
||||
'deliverables': 'E-commerce Website, Admin Dashboard, Payment Integration, Inventory Management, Order Management, Customer Portal, Analytics Dashboard, SEO Optimization, Mobile App, Testing Suite, Documentation, 6 Months Support',
|
||||
'technologies': 'React, Next.js, Node.js, PostgreSQL, Stripe, PayPal, AWS, Redis, Elasticsearch, Jest, Cypress',
|
||||
'process_steps': 'Requirements Analysis, UI/UX Design, Database Design, Backend Development, Frontend Development, Payment Integration, Testing, SEO Optimization, Performance Tuning, Launch, Marketing Setup, Support',
|
||||
'featured': False,
|
||||
'deliverables': 'Frontend Application, Component Library, Responsive Design, Accessibility Implementation, Performance Optimization, Testing Suite, Documentation, Style Guide, 6 Months Support',
|
||||
'technologies': 'React, Next.js, TypeScript, Vue.js, Tailwind CSS, Material-UI, Redux, GraphQL, Jest, Cypress, Webpack, Vite',
|
||||
'process_steps': 'UI/UX Design, Component Development, Responsive Implementation, Accessibility Audit, Performance Optimization, Testing, Browser Compatibility, Deployment',
|
||||
'featured': True,
|
||||
'display_order': 6,
|
||||
'features': [
|
||||
{'title': 'Secure Payments', 'description': 'Multiple payment gateways with PCI compliance', 'icon': 'credit-card'},
|
||||
{'title': 'Inventory Management', 'description': 'Advanced inventory tracking and management', 'icon': 'box'},
|
||||
{'title': 'Customer Analytics', 'description': 'Detailed customer behavior and sales analytics', 'icon': 'chart-bar'},
|
||||
{'title': 'Mobile Optimized', 'description': 'Fully responsive design for mobile shopping', 'icon': 'mobile'},
|
||||
{'title': 'SEO Ready', 'description': 'Built-in SEO optimization for better visibility', 'icon': 'search'},
|
||||
{'title': 'Multi-vendor Support', 'description': 'Support for multiple vendors and marketplace', 'icon': 'store'}
|
||||
{'title': 'Responsive Design', 'description': 'Flawless experience across all devices', 'icon': 'mobile'},
|
||||
{'title': 'Accessible', 'description': 'WCAG compliant for all users', 'icon': 'universal-access'},
|
||||
{'title': 'Engaging UX', 'description': 'Beautiful, intuitive user experiences', 'icon': 'heart'},
|
||||
{'title': 'High Performance', 'description': 'Optimized for speed and efficiency', 'icon': 'bolt'},
|
||||
{'title': 'Modern Design', 'description': 'Contemporary design patterns and aesthetics', 'icon': 'palette'},
|
||||
{'title': 'Cross-Platform', 'description': 'Works seamlessly across browsers and platforms', 'icon': 'window-maximize'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'External Systems Integrations',
|
||||
'description': 'We connect everything — from fiscal printers and payment terminals to ERP and cloud platforms — enabling enterprises to operate seamlessly across physical and digital environments.',
|
||||
'short_description': 'Connect systems for seamless enterprise operations.',
|
||||
'slug': 'external-systems-integrations',
|
||||
'icon': 'plug',
|
||||
'price': '45000.00',
|
||||
'category_slug': 'enterprise-content',
|
||||
'duration': '12-18 weeks',
|
||||
'deliverables': 'Integration Platform, API Connectors, Payment Gateway Integration, ERP Integration, Device Integration, Middleware Development, Testing Suite, Security Implementation, Documentation, 6 Months Support',
|
||||
'technologies': 'REST APIs, GraphQL, SOAP, gRPC, Kafka, RabbitMQ, OAuth 2.0, SAP, Salesforce, Stripe, PayPal, IoT Protocols, Node.js, Python',
|
||||
'process_steps': 'Systems Analysis, Integration Design, API Development, Device Setup, Testing, Security Review, Deployment, Monitoring, Documentation, Training',
|
||||
'featured': True,
|
||||
'display_order': 7,
|
||||
'features': [
|
||||
{'title': 'Universal Connectivity', 'description': 'Connect any system, device, or platform', 'icon': 'network-wired'},
|
||||
{'title': 'Payment Integration', 'description': 'Payment terminals and gateway integration', 'icon': 'credit-card'},
|
||||
{'title': 'ERP Integration', 'description': 'Seamless ERP and business system integration', 'icon': 'building'},
|
||||
{'title': 'IoT Devices', 'description': 'Connect physical devices like fiscal printers', 'icon': 'print'},
|
||||
{'title': 'Cloud Platforms', 'description': 'Integration with major cloud platforms', 'icon': 'cloud'},
|
||||
{'title': 'Seamless Operations', 'description': 'Unified operations across all environments', 'icon': 'sync'}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -57,11 +57,6 @@ export default function ServicesList() {
|
||||
)}
|
||||
</div>
|
||||
<p className="card-text">{service.description}</p>
|
||||
{service.price && (
|
||||
<p className="text-muted">
|
||||
<strong>Starting at: ${service.price}</strong>
|
||||
</p>
|
||||
)}
|
||||
<div className="mt-auto">
|
||||
<a
|
||||
href={`/services/${service.slug}`}
|
||||
|
||||
@@ -227,18 +227,12 @@ const AboutBanner = () => {
|
||||
|
||||
{/* Social Links */}
|
||||
<div className="social-links">
|
||||
<Link href="https://www.linkedin.com/company/enterprisesoft-solutions" target="_blank" className="social-link">
|
||||
<Link href="https://www.linkedin.com/company/gnxtech" target="_blank" className="social-link">
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
</Link>
|
||||
<Link href="https://github.com/enterprisesoft" target="_blank" className="social-link">
|
||||
<Link href="https://github.com/gnxtech" target="_blank" className="social-link">
|
||||
<i className="fa-brands fa-github"></i>
|
||||
</Link>
|
||||
<Link href="https://www.twitter.com/enterprisesoft" target="_blank" className="social-link">
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
<Link href="https://stackoverflow.com/teams/enterprisesoft" target="_blank" className="social-link">
|
||||
<i className="fa-brands fa-stack-overflow"></i>
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -81,10 +81,10 @@ const AboutServiceComponent = () => {
|
||||
{serviceData?.badge_text || "About Our Company"}
|
||||
</div>
|
||||
<h2 className="title-anim">
|
||||
{serviceData?.title || "Enterprise Technology Leaders"}
|
||||
{serviceData?.title || "GNX Soft Ltd. - Software Excellence"}
|
||||
</h2>
|
||||
<p>
|
||||
{serviceData?.description || "Founded 2008, EnterpriseSoft Solutions has emerged as a premier enterprise software company, serving Fortune 500 companies and innovative startups worldwide. Our team of 200+ engineers, architects, and consultants specializes in delivering mission-critical software solutions that drive digital transformation and business growth."}
|
||||
{serviceData?.description || "Founded in 2020, GNX Soft Ltd. has emerged as a premier enterprise software company, delivering mission-critical software solutions across various industries. Our team of expert engineers, architects, and consultants specializes in custom software development, data replication, incident management, and comprehensive system integrations that drive digital transformation and business growth."}
|
||||
</p>
|
||||
<div className="enterprise-features">
|
||||
<div className="row">
|
||||
@@ -144,7 +144,7 @@ const AboutServiceComponent = () => {
|
||||
</div>
|
||||
<div className="feature-content">
|
||||
<h6>Global Reach</h6>
|
||||
<p>Offices in 5 Countries</p>
|
||||
<p>Based in Bulgaria, Serving Worldwide</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -208,26 +208,6 @@ const BlogSingle = () => {
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
<span>LinkedIn</span>
|
||||
</Link>
|
||||
<Link
|
||||
href={`https://twitter.com/intent/tweet?url=${typeof window !== 'undefined' ? encodeURIComponent(window.location.href) : ''}&text=${encodeURIComponent(post.title)}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="share-btn share-twitter"
|
||||
aria-label="Share on Twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
<span>Twitter</span>
|
||||
</Link>
|
||||
<Link
|
||||
href={`https://www.facebook.com/sharer/sharer.php?u=${typeof window !== 'undefined' ? encodeURIComponent(window.location.href) : ''}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="share-btn share-facebook"
|
||||
aria-label="Share on Facebook"
|
||||
>
|
||||
<i className="fa-brands fa-facebook-f"></i>
|
||||
<span>Facebook</span>
|
||||
</Link>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (typeof window !== 'undefined' && navigator.clipboard) {
|
||||
|
||||
@@ -114,38 +114,20 @@ const CareerBanner = () => {
|
||||
<ul className="social">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.facebook.com/"
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="share us on facebook"
|
||||
>
|
||||
<i className="fa-brands fa-facebook-f"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.twitter.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.pinterest.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on pinterest"
|
||||
aria-label="connect with us on linkedin"
|
||||
>
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.instagram.com/"
|
||||
href="https://github.com/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="share us on instagram"
|
||||
aria-label="view our code on github"
|
||||
>
|
||||
<i className="fa-brands fa-instagram"></i>
|
||||
<i className="fa-brands fa-github"></i>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -59,7 +59,7 @@ const ServiceDetails = ({ service }: ServiceDetailsProps) => {
|
||||
|
||||
<div className="enterprise-stats mb-4">
|
||||
<div className="row g-3">
|
||||
<div className="col-6">
|
||||
<div className="col-12">
|
||||
<div className="enterprise-stat-card">
|
||||
<div className="stat-icon">
|
||||
<i className="fa-solid fa-star"></i>
|
||||
@@ -72,19 +72,6 @@ const ServiceDetails = ({ service }: ServiceDetailsProps) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-6">
|
||||
<div className="enterprise-stat-card">
|
||||
<div className="stat-icon">
|
||||
<i className="fa-solid fa-dollar-sign"></i>
|
||||
</div>
|
||||
<div className="stat-content">
|
||||
<div className="stat-number">
|
||||
{service.formatted_price}
|
||||
</div>
|
||||
<div className="stat-label">Starting Price</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -137,9 +137,6 @@ const ServiceMain = () => {
|
||||
</p>
|
||||
|
||||
<div className="service-footer">
|
||||
<div className="service-price">
|
||||
{service.formatted_price}
|
||||
</div>
|
||||
<Link href={`/services/${service.slug}`} className="service-link">
|
||||
<span>View Details</span>
|
||||
<i className="fa-solid fa-arrow-right"></i>
|
||||
|
||||
@@ -61,11 +61,8 @@ const ServicePricing = ({ service }: ServicePricingProps) => {
|
||||
{service.title}
|
||||
</h3>
|
||||
<div className="price-display">
|
||||
<span className="price-amount">
|
||||
{service.formatted_price}
|
||||
</span>
|
||||
<span className="price-period">
|
||||
Starting Price
|
||||
Contact Us for Pricing
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -85,38 +85,20 @@ const ServicesBanner = () => {
|
||||
<ul className="social">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.facebook.com/"
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="share us on facebook"
|
||||
>
|
||||
<i className="fa-brands fa-facebook-f"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.twitter.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.pinterest.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on pinterest"
|
||||
aria-label="connect with us on linkedin"
|
||||
>
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.instagram.com/"
|
||||
href="https://github.com/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="share us on instagram"
|
||||
aria-label="view our code on github"
|
||||
>
|
||||
<i className="fa-brands fa-instagram"></i>
|
||||
<i className="fa-brands fa-github"></i>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -48,14 +48,6 @@ const Transform = ({ service }: TransformProps) => {
|
||||
<p className="enterprise-section-description">
|
||||
{service.description}
|
||||
</p>
|
||||
{service.formatted_price && (
|
||||
<div className="mt-4">
|
||||
<div className="price-highlight">
|
||||
<span className="price-label">Starting from</span>
|
||||
<span className="price-value">{service.formatted_price}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
207
gnx-react/components/shared/OptimizedImage.tsx
Normal file
207
gnx-react/components/shared/OptimizedImage.tsx
Normal file
@@ -0,0 +1,207 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface OptimizedImageProps {
|
||||
src: string;
|
||||
alt: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
className?: string;
|
||||
priority?: boolean;
|
||||
fill?: boolean;
|
||||
sizes?: string;
|
||||
quality?: number;
|
||||
style?: React.CSSProperties;
|
||||
objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
loading?: 'lazy' | 'eager';
|
||||
}
|
||||
|
||||
/**
|
||||
* OptimizedImage Component
|
||||
*
|
||||
* An enterprise-grade optimized image component that provides:
|
||||
* - Automatic lazy loading
|
||||
* - Responsive images with srcset
|
||||
* - WebP/AVIF format support
|
||||
* - Blur placeholder while loading
|
||||
* - Error handling with fallback
|
||||
* - Performance optimization
|
||||
*
|
||||
* @example
|
||||
* <OptimizedImage
|
||||
* src="/images/hero.jpg"
|
||||
* alt="Hero banner showcasing our services"
|
||||
* width={1200}
|
||||
* height={600}
|
||||
* priority={true}
|
||||
* />
|
||||
*/
|
||||
export default function OptimizedImage({
|
||||
src,
|
||||
alt,
|
||||
width,
|
||||
height,
|
||||
className = '',
|
||||
priority = false,
|
||||
fill = false,
|
||||
sizes,
|
||||
quality = 85,
|
||||
style,
|
||||
objectFit = 'cover',
|
||||
loading,
|
||||
}: OptimizedImageProps) {
|
||||
const [imgSrc, setImgSrc] = useState(src);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
|
||||
// Fallback image for errors
|
||||
const fallbackImage = '/images/placeholder.png';
|
||||
|
||||
// Handle image load
|
||||
const handleLoad = () => {
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
// Handle image error
|
||||
const handleError = () => {
|
||||
setHasError(true);
|
||||
setIsLoading(false);
|
||||
if (imgSrc !== fallbackImage) {
|
||||
setImgSrc(fallbackImage);
|
||||
}
|
||||
};
|
||||
|
||||
// SEO-friendly alt text validation
|
||||
const seoAlt = alt || 'GNX Soft - Enterprise Software Solutions';
|
||||
|
||||
// Validate alt text for SEO
|
||||
if (process.env.NODE_ENV === 'development' && !alt) {
|
||||
console.warn(
|
||||
`OptimizedImage: Missing alt text for image "${src}". Alt text is crucial for SEO and accessibility.`
|
||||
);
|
||||
}
|
||||
|
||||
// Common image props
|
||||
const imageProps = {
|
||||
src: imgSrc,
|
||||
alt: seoAlt,
|
||||
className: `${className} ${isLoading ? 'image-loading' : 'image-loaded'}`,
|
||||
onLoad: handleLoad,
|
||||
onError: handleError,
|
||||
quality,
|
||||
loading: loading || (priority ? 'eager' : 'lazy'),
|
||||
style: {
|
||||
...style,
|
||||
objectFit: objectFit as any,
|
||||
},
|
||||
};
|
||||
|
||||
// Use fill layout for responsive images
|
||||
if (fill) {
|
||||
return (
|
||||
<div className={`optimized-image-wrapper ${hasError ? 'has-error' : ''}`}>
|
||||
<Image
|
||||
{...imageProps}
|
||||
fill
|
||||
sizes={sizes || '100vw'}
|
||||
priority={priority}
|
||||
/>
|
||||
<style jsx>{`
|
||||
.optimized-image-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.optimized-image-wrapper.has-error {
|
||||
background: #f3f4f6;
|
||||
}
|
||||
:global(.image-loading) {
|
||||
filter: blur(10px);
|
||||
transform: scale(1.1);
|
||||
transition: filter 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
:global(.image-loaded) {
|
||||
filter: blur(0);
|
||||
transform: scale(1);
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Standard layout with explicit dimensions
|
||||
return (
|
||||
<div className={`optimized-image-container ${hasError ? 'has-error' : ''}`}>
|
||||
<Image
|
||||
{...imageProps}
|
||||
width={width}
|
||||
height={height}
|
||||
sizes={sizes}
|
||||
priority={priority}
|
||||
/>
|
||||
<style jsx>{`
|
||||
.optimized-image-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.optimized-image-container.has-error {
|
||||
background: #f3f4f6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
:global(.image-loading) {
|
||||
filter: blur(10px);
|
||||
transform: scale(1.05);
|
||||
transition: filter 0.4s ease, transform 0.4s ease;
|
||||
}
|
||||
:global(.image-loaded) {
|
||||
filter: blur(0);
|
||||
transform: scale(1);
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage Examples:
|
||||
*
|
||||
* 1. Hero Image (Priority Loading):
|
||||
* <OptimizedImage
|
||||
* src="/images/hero.jpg"
|
||||
* alt="Enterprise software development solutions"
|
||||
* width={1920}
|
||||
* height={1080}
|
||||
* priority={true}
|
||||
* sizes="100vw"
|
||||
* />
|
||||
*
|
||||
* 2. Service Card Image (Lazy Loading):
|
||||
* <OptimizedImage
|
||||
* src="/images/service/custom-software.jpg"
|
||||
* alt="Custom software development service icon"
|
||||
* width={400}
|
||||
* height={300}
|
||||
* sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
* />
|
||||
*
|
||||
* 3. Background Image (Fill):
|
||||
* <OptimizedImage
|
||||
* src="/images/background.jpg"
|
||||
* alt="Technology background pattern"
|
||||
* fill={true}
|
||||
* sizes="100vw"
|
||||
* objectFit="cover"
|
||||
* />
|
||||
*
|
||||
* 4. Logo (High Priority):
|
||||
* <OptimizedImage
|
||||
* src="/images/logo.png"
|
||||
* alt="GNX Soft company logo"
|
||||
* width={200}
|
||||
* height={50}
|
||||
* priority={true}
|
||||
* quality={100}
|
||||
* />
|
||||
*/
|
||||
|
||||
57
gnx-react/components/shared/ProtectedImage.tsx
Normal file
57
gnx-react/components/shared/ProtectedImage.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
'use client';
|
||||
|
||||
import { ReactNode, CSSProperties } from 'react';
|
||||
|
||||
interface ProtectedImageProps {
|
||||
src: string;
|
||||
alt: string;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
width?: number;
|
||||
height?: number;
|
||||
showWatermark?: boolean;
|
||||
priority?: boolean;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected Image Component
|
||||
* Wraps images with protection against downloading and copying
|
||||
*/
|
||||
export default function ProtectedImage({
|
||||
src,
|
||||
alt,
|
||||
className = '',
|
||||
style = {},
|
||||
width,
|
||||
height,
|
||||
showWatermark = false,
|
||||
children
|
||||
}: ProtectedImageProps) {
|
||||
const wrapperClass = `protected-image-wrapper ${showWatermark ? 'watermarked-image' : ''} ${className}`;
|
||||
|
||||
return (
|
||||
<div className={wrapperClass} style={style}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
width={width}
|
||||
height={height}
|
||||
draggable="false"
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
onDragStart={(e) => e.preventDefault()}
|
||||
style={{
|
||||
WebkitUserSelect: 'none',
|
||||
MozUserSelect: 'none',
|
||||
msUserSelect: 'none',
|
||||
userSelect: 'none',
|
||||
pointerEvents: 'none'
|
||||
}}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -309,19 +309,6 @@ const ServiceDetailsBanner = ({ service }: ServiceDetailsBannerProps) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{service.formatted_price && (
|
||||
<div className="col-auto">
|
||||
<div className="highlight-card">
|
||||
<div className="highlight-icon">
|
||||
<i className="fa-solid fa-dollar-sign"></i>
|
||||
</div>
|
||||
<div className="highlight-content">
|
||||
<span className="highlight-label">Starting From</span>
|
||||
<span className="highlight-value">{service.formatted_price}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{service.featured && (
|
||||
<div className="col-auto">
|
||||
<div className="highlight-card featured">
|
||||
@@ -355,38 +342,20 @@ const ServiceDetailsBanner = ({ service }: ServiceDetailsBannerProps) => {
|
||||
<ul className="social">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.facebook.com/"
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="share us on facebook"
|
||||
>
|
||||
<i className="fa-brands fa-facebook-f"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.twitter.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.pinterest.com/"
|
||||
target="_blank"
|
||||
aria-label="share us on pinterest"
|
||||
aria-label="connect with us on linkedin"
|
||||
>
|
||||
<i className="fa-brands fa-linkedin-in"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.instagram.com/"
|
||||
href="https://github.com/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="share us on instagram"
|
||||
aria-label="view our code on github"
|
||||
>
|
||||
<i className="fa-brands fa-instagram"></i>
|
||||
<i className="fa-brands fa-github"></i>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -243,40 +243,19 @@ const Footer = () => {
|
||||
<div className="col-12 col-lg-6">
|
||||
<div className="social justify-content-center justify-content-lg-end">
|
||||
<Link
|
||||
href="https://www.linkedin.com/company/itify"
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
target="_blank"
|
||||
title="LinkedIn"
|
||||
>
|
||||
<i className="fa-brands fa-linkedin"></i>
|
||||
</Link>
|
||||
<Link
|
||||
href="https://github.com/itify"
|
||||
href="https://github.com/gnxtech"
|
||||
target="_blank"
|
||||
title="GitHub"
|
||||
>
|
||||
<i className="fa-brands fa-github"></i>
|
||||
</Link>
|
||||
<Link
|
||||
href="https://www.twitter.com/itify"
|
||||
target="_blank"
|
||||
title="Twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
<Link
|
||||
href="https://www.youtube.com/c/itify"
|
||||
target="_blank"
|
||||
title="YouTube"
|
||||
>
|
||||
<i className="fa-brands fa-youtube"></i>
|
||||
</Link>
|
||||
<Link
|
||||
href="https://stackoverflow.com/teams/itify"
|
||||
target="_blank"
|
||||
title="Stack Overflow"
|
||||
>
|
||||
<i className="fa-brands fa-stack-overflow"></i>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -36,7 +36,7 @@ const Header = () => {
|
||||
created_at: service.created_at,
|
||||
updated_at: service.updated_at
|
||||
}))
|
||||
};
|
||||
} as any;
|
||||
} else {
|
||||
console.log('Using static services data. API services:', apiServices.length, 'Services index:', servicesIndex);
|
||||
}
|
||||
@@ -167,30 +167,30 @@ const Header = () => {
|
||||
{/* Desktop Navigation Menu */}
|
||||
<div className="navbar__menu d-none d-lg-flex">
|
||||
<ul>
|
||||
{navigationData.map((item, index) =>
|
||||
item.submenu ? (
|
||||
{navigationData.map((item) =>
|
||||
item.title === "Support Center" ? null : item.submenu ? (
|
||||
<li
|
||||
className="navbar__item navbar__item--has-children"
|
||||
key={index}
|
||||
onMouseEnter={() => !isMobile && setOpenDropdown(index)}
|
||||
key={item.id}
|
||||
onMouseEnter={() => !isMobile && setOpenDropdown(item.id)}
|
||||
onMouseLeave={() => !isMobile && setOpenDropdown(null)}
|
||||
>
|
||||
<button
|
||||
aria-label="dropdown menu"
|
||||
className={
|
||||
"navbar__dropdown-label" +
|
||||
(openDropdown === index
|
||||
(openDropdown === item.id
|
||||
? " navbar__item-active"
|
||||
: " ")
|
||||
}
|
||||
onClick={() => isMobile && handleDropdownToggle(index)}
|
||||
onClick={() => isMobile && handleDropdownToggle(item.id)}
|
||||
>
|
||||
{item.title}
|
||||
{item.title === "Services" && servicesLoading && (
|
||||
<span className="loading-indicator">⏳</span>
|
||||
)}
|
||||
</button>
|
||||
<ul className={`navbar__sub-menu ${openDropdown === index ? 'show' : ''}`}>
|
||||
<ul className={`navbar__sub-menu ${openDropdown === item.id ? 'show' : ''}`}>
|
||||
{item.title === "Services" && servicesLoading ? (
|
||||
<li>
|
||||
<span className="text-muted">Loading services...</span>
|
||||
@@ -218,7 +218,10 @@ const Header = () => {
|
||||
</ul>
|
||||
</li>
|
||||
) : (
|
||||
<li className="navbar__item" key={index}>
|
||||
<li
|
||||
className="navbar__item"
|
||||
key={item.id}
|
||||
>
|
||||
<Link
|
||||
href={item.path || "#"}
|
||||
className={
|
||||
|
||||
@@ -25,12 +25,18 @@ const OffcanvasMenu = ({
|
||||
servicesError = null
|
||||
}: OffcanvasMenuProps) => {
|
||||
const [openDropdown, setOpenDropdown] = useState(null);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
const handleDropdownToggle = (index: any) => {
|
||||
setOpenDropdown((prev) => (prev === index ? null : index));
|
||||
};
|
||||
|
||||
const pathname = usePathname();
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const parentItems = document.querySelectorAll(
|
||||
".navbar__item--has-children"
|
||||
@@ -51,6 +57,7 @@ const OffcanvasMenu = ({
|
||||
className={
|
||||
"offcanvas-menu" + (isOffcanvasOpen ? " show-offcanvas-menu" : " ")
|
||||
}
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<nav
|
||||
className={
|
||||
@@ -115,7 +122,7 @@ const OffcanvasMenu = ({
|
||||
<Link
|
||||
href={subItem.path || "#"}
|
||||
className={
|
||||
pathname === subItem.path
|
||||
mounted && pathname === subItem.path
|
||||
? " active-current-sub"
|
||||
: " "
|
||||
}
|
||||
@@ -133,7 +140,7 @@ const OffcanvasMenu = ({
|
||||
<Link
|
||||
href={item.path || "#"}
|
||||
className={
|
||||
pathname === item.path ? " active-current-link" : " "
|
||||
mounted && pathname === item.path ? " active-current-link" : " "
|
||||
}
|
||||
>
|
||||
{item.title}
|
||||
@@ -150,13 +157,13 @@ const OffcanvasMenu = ({
|
||||
<h4>Get in Touch</h4>
|
||||
<p>Ready to transform your business?</p>
|
||||
<div className="contact-methods">
|
||||
<a href="tel:+1-800-ENTERPRISE" className="contact-item">
|
||||
<a href="tel:+359896138030" className="contact-item">
|
||||
<i className="fa-solid fa-phone"></i>
|
||||
<span>+1 (800) ENTERPRISE</span>
|
||||
<span>+359896138030</span>
|
||||
</a>
|
||||
<a href="mailto:solutions@enterprise.com" className="contact-item">
|
||||
<a href="mailto:info@gnxsoft.com" className="contact-item">
|
||||
<i className="fa-solid fa-envelope"></i>
|
||||
<span>solutions@enterprise.com</span>
|
||||
<span>info@gnxsoft.com</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -164,7 +171,7 @@ const OffcanvasMenu = ({
|
||||
<ul className="enterprise-social nav-fade">
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.linkedin.com/company/enterprise"
|
||||
href="https://www.linkedin.com/company/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="Connect with us on LinkedIn"
|
||||
>
|
||||
@@ -173,25 +180,7 @@ const OffcanvasMenu = ({
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.twitter.com/enterprise"
|
||||
target="_blank"
|
||||
aria-label="Follow us on Twitter"
|
||||
>
|
||||
<i className="fa-brands fa-twitter"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://www.youtube.com/enterprise"
|
||||
target="_blank"
|
||||
aria-label="Watch our videos on YouTube"
|
||||
>
|
||||
<i className="fa-brands fa-youtube"></i>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://github.com/enterprise"
|
||||
href="https://github.com/gnxtech"
|
||||
target="_blank"
|
||||
aria-label="View our code on GitHub"
|
||||
>
|
||||
|
||||
378
gnx-react/components/shared/seo/StructuredData.tsx
Normal file
378
gnx-react/components/shared/seo/StructuredData.tsx
Normal file
@@ -0,0 +1,378 @@
|
||||
'use client';
|
||||
|
||||
import Script from 'next/script';
|
||||
import { SITE_CONFIG } from '@/lib/seo/metadata';
|
||||
|
||||
// Organization Schema
|
||||
export function OrganizationSchema() {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Organization',
|
||||
name: SITE_CONFIG.name,
|
||||
legalName: `${SITE_CONFIG.name} LLC`,
|
||||
url: SITE_CONFIG.url,
|
||||
logo: `${SITE_CONFIG.url}/images/logo.png`,
|
||||
foundingDate: SITE_CONFIG.foundedYear.toString(),
|
||||
description: SITE_CONFIG.description,
|
||||
email: SITE_CONFIG.email,
|
||||
telephone: SITE_CONFIG.phone,
|
||||
address: {
|
||||
'@type': 'PostalAddress',
|
||||
streetAddress: SITE_CONFIG.address.street,
|
||||
addressLocality: SITE_CONFIG.address.city,
|
||||
addressRegion: SITE_CONFIG.address.state,
|
||||
postalCode: SITE_CONFIG.address.zip,
|
||||
addressCountry: SITE_CONFIG.address.country,
|
||||
},
|
||||
sameAs: [
|
||||
SITE_CONFIG.social.linkedin,
|
||||
SITE_CONFIG.social.github,
|
||||
],
|
||||
contactPoint: [
|
||||
{
|
||||
'@type': 'ContactPoint',
|
||||
telephone: SITE_CONFIG.phone,
|
||||
contactType: 'Customer Service',
|
||||
email: SITE_CONFIG.email,
|
||||
availableLanguage: ['English'],
|
||||
areaServed: 'Worldwide',
|
||||
},
|
||||
{
|
||||
'@type': 'ContactPoint',
|
||||
telephone: SITE_CONFIG.phone,
|
||||
contactType: 'Sales',
|
||||
email: `sales@${SITE_CONFIG.email.split('@')[1]}`,
|
||||
availableLanguage: ['English'],
|
||||
},
|
||||
{
|
||||
'@type': 'ContactPoint',
|
||||
telephone: SITE_CONFIG.phone,
|
||||
contactType: 'Technical Support',
|
||||
email: `support@${SITE_CONFIG.email.split('@')[1]}`,
|
||||
availableLanguage: ['English'],
|
||||
areaServed: 'Worldwide',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id="organization-schema"
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Website Schema
|
||||
export function WebsiteSchema() {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'WebSite',
|
||||
name: SITE_CONFIG.name,
|
||||
url: SITE_CONFIG.url,
|
||||
description: SITE_CONFIG.description,
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: SITE_CONFIG.name,
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: `${SITE_CONFIG.url}/images/logo.png`,
|
||||
},
|
||||
},
|
||||
potentialAction: {
|
||||
'@type': 'SearchAction',
|
||||
target: {
|
||||
'@type': 'EntryPoint',
|
||||
urlTemplate: `${SITE_CONFIG.url}/search?q={search_term_string}`,
|
||||
},
|
||||
'query-input': 'required name=search_term_string',
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id="website-schema"
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Breadcrumb Schema
|
||||
interface BreadcrumbItem {
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface BreadcrumbSchemaProps {
|
||||
items: BreadcrumbItem[];
|
||||
}
|
||||
|
||||
export function BreadcrumbSchema({ items }: BreadcrumbSchemaProps) {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: items.map((item, index) => ({
|
||||
'@type': 'ListItem',
|
||||
position: index + 1,
|
||||
name: item.name,
|
||||
item: `${SITE_CONFIG.url}${item.url}`,
|
||||
})),
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id="breadcrumb-schema"
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Service Schema
|
||||
interface ServiceSchemaProps {
|
||||
service: {
|
||||
title: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
category?: { name: string };
|
||||
duration?: string;
|
||||
technologies?: string;
|
||||
deliverables?: string;
|
||||
image?: string | File;
|
||||
image_url?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function ServiceSchema({ service }: ServiceSchemaProps) {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Service',
|
||||
name: service.title,
|
||||
description: service.description,
|
||||
provider: {
|
||||
'@type': 'Organization',
|
||||
name: SITE_CONFIG.name,
|
||||
url: SITE_CONFIG.url,
|
||||
},
|
||||
serviceType: service.category?.name || 'Enterprise Software',
|
||||
areaServed: {
|
||||
'@type': 'Country',
|
||||
name: 'Worldwide',
|
||||
},
|
||||
url: `${SITE_CONFIG.url}/services/${service.slug}`,
|
||||
image: service.image_url ||
|
||||
(typeof service.image === 'string' ? `${SITE_CONFIG.url}${service.image}` : `${SITE_CONFIG.url}/images/service/default.png`),
|
||||
serviceOutput: service.deliverables,
|
||||
aggregateRating: {
|
||||
'@type': 'AggregateRating',
|
||||
ratingValue: '4.9',
|
||||
ratingCount: '127',
|
||||
bestRating: '5',
|
||||
worstRating: '1',
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id={`service-schema-${service.slug}`}
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Article Schema (for blog posts)
|
||||
interface ArticleSchemaProps {
|
||||
article: {
|
||||
title: string;
|
||||
description?: string;
|
||||
excerpt?: string;
|
||||
slug: string;
|
||||
image?: string;
|
||||
published_at?: string;
|
||||
updated_at?: string;
|
||||
author?: { name: string; image?: string };
|
||||
category?: { name: string };
|
||||
};
|
||||
}
|
||||
|
||||
export function ArticleSchema({ article }: ArticleSchemaProps) {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Article',
|
||||
headline: article.title,
|
||||
description: article.description || article.excerpt,
|
||||
image: article.image
|
||||
? `${SITE_CONFIG.url}${article.image}`
|
||||
: `${SITE_CONFIG.url}/images/blog/default.png`,
|
||||
datePublished: article.published_at,
|
||||
dateModified: article.updated_at || article.published_at,
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: article.author?.name || SITE_CONFIG.name,
|
||||
image: article.author?.image,
|
||||
},
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: SITE_CONFIG.name,
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: `${SITE_CONFIG.url}/images/logo.png`,
|
||||
},
|
||||
},
|
||||
articleSection: article.category?.name,
|
||||
url: `${SITE_CONFIG.url}/insights/${article.slug}`,
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id={`article-schema-${article.slug}`}
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// FAQ Schema
|
||||
interface FAQItem {
|
||||
question: string;
|
||||
answer: string;
|
||||
}
|
||||
|
||||
interface FAQSchemaProps {
|
||||
faqs: FAQItem[];
|
||||
}
|
||||
|
||||
export function FAQSchema({ faqs }: FAQSchemaProps) {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'FAQPage',
|
||||
mainEntity: faqs.map((faq) => ({
|
||||
'@type': 'Question',
|
||||
name: faq.question,
|
||||
acceptedAnswer: {
|
||||
'@type': 'Answer',
|
||||
text: faq.answer,
|
||||
},
|
||||
})),
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id="faq-schema"
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Job Posting Schema
|
||||
interface JobPostingSchemaProps {
|
||||
job: {
|
||||
title: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
location?: string;
|
||||
employment_type?: string;
|
||||
salary_min?: number;
|
||||
salary_max?: number;
|
||||
posted_at?: string;
|
||||
valid_through?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function JobPostingSchema({ job }: JobPostingSchemaProps) {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'JobPosting',
|
||||
title: job.title,
|
||||
description: job.description,
|
||||
datePosted: job.posted_at || new Date().toISOString(),
|
||||
validThrough: job.valid_through,
|
||||
employmentType: job.employment_type || 'FULL_TIME',
|
||||
hiringOrganization: {
|
||||
'@type': 'Organization',
|
||||
name: SITE_CONFIG.name,
|
||||
sameAs: SITE_CONFIG.url,
|
||||
logo: `${SITE_CONFIG.url}/images/logo.png`,
|
||||
},
|
||||
jobLocation: {
|
||||
'@type': 'Place',
|
||||
address: {
|
||||
'@type': 'PostalAddress',
|
||||
addressLocality: job.location || SITE_CONFIG.address.city,
|
||||
addressRegion: SITE_CONFIG.address.state,
|
||||
addressCountry: SITE_CONFIG.address.country,
|
||||
},
|
||||
},
|
||||
baseSalary: job.salary_min &&
|
||||
job.salary_max && {
|
||||
'@type': 'MonetaryAmount',
|
||||
currency: 'USD',
|
||||
value: {
|
||||
'@type': 'QuantitativeValue',
|
||||
minValue: job.salary_min,
|
||||
maxValue: job.salary_max,
|
||||
unitText: 'YEAR',
|
||||
},
|
||||
},
|
||||
url: `${SITE_CONFIG.url}/career/${job.slug}`,
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id={`job-posting-schema-${job.slug}`}
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Local Business Schema
|
||||
export function LocalBusinessSchema() {
|
||||
const schema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'ProfessionalService',
|
||||
name: SITE_CONFIG.name,
|
||||
image: `${SITE_CONFIG.url}/images/logo.png`,
|
||||
'@id': SITE_CONFIG.url,
|
||||
url: SITE_CONFIG.url,
|
||||
telephone: SITE_CONFIG.phone,
|
||||
priceRange: '$$$$',
|
||||
address: {
|
||||
'@type': 'PostalAddress',
|
||||
streetAddress: SITE_CONFIG.address.street,
|
||||
addressLocality: SITE_CONFIG.address.city,
|
||||
addressRegion: SITE_CONFIG.address.state,
|
||||
postalCode: SITE_CONFIG.address.zip,
|
||||
addressCountry: SITE_CONFIG.address.country,
|
||||
},
|
||||
geo: {
|
||||
'@type': 'GeoCoordinates',
|
||||
latitude: 37.7749,
|
||||
longitude: -122.4194,
|
||||
},
|
||||
openingHoursSpecification: {
|
||||
'@type': 'OpeningHoursSpecification',
|
||||
dayOfWeek: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
|
||||
opens: '09:00',
|
||||
closes: '18:00',
|
||||
},
|
||||
aggregateRating: {
|
||||
'@type': 'AggregateRating',
|
||||
ratingValue: '4.9',
|
||||
reviewCount: '127',
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Script
|
||||
id="local-business-schema"
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
/**
|
||||
* API Configuration
|
||||
* Centralized configuration for API endpoints
|
||||
*
|
||||
* In Development: Calls backend directly at http://localhost:8000
|
||||
* In Production: Uses Next.js rewrites/nginx proxy at /api (internal network only)
|
||||
*/
|
||||
|
||||
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
|
||||
// Production: Use relative URLs (nginx proxy)
|
||||
// Development: Use full backend URL
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
export const API_BASE_URL = isProduction
|
||||
? '' // Use relative URLs in production (proxied by nginx)
|
||||
: (process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000');
|
||||
|
||||
export const API_CONFIG = {
|
||||
// Django API Base URL
|
||||
|
||||
297
gnx-react/lib/seo/metadata.ts
Normal file
297
gnx-react/lib/seo/metadata.ts
Normal file
@@ -0,0 +1,297 @@
|
||||
import { Metadata } from 'next';
|
||||
|
||||
// Site Configuration
|
||||
export const SITE_CONFIG = {
|
||||
name: 'GNX Soft',
|
||||
shortName: 'GNX',
|
||||
description: 'Leading enterprise software development company providing custom solutions, data replication, incident management, AI-powered business intelligence, and comprehensive system integrations for modern businesses.',
|
||||
url: process.env.NEXT_PUBLIC_SITE_URL || 'https://gnxsoft.com',
|
||||
ogImage: '/images/og-image.png',
|
||||
email: 'info@gnxsoft.com',
|
||||
phone: '+359 896 13 80 30',
|
||||
address: {
|
||||
street: 'Tsar Simeon I, 56',
|
||||
city: 'Burgas',
|
||||
state: 'BG',
|
||||
zip: '8000',
|
||||
country: 'Bulgaria',
|
||||
},
|
||||
social: {
|
||||
linkedin: 'https://www.linkedin.com/company/gnxtech',
|
||||
github: 'https://github.com/gnxtech',
|
||||
},
|
||||
businessHours: 'Monday - Friday: 9:00 AM - 6:00 PM PST',
|
||||
foundedYear: 2020,
|
||||
};
|
||||
|
||||
// Default SEO Configuration
|
||||
export const DEFAULT_SEO = {
|
||||
title: `${SITE_CONFIG.name} | Enterprise Software Development & IT Solutions`,
|
||||
description: SITE_CONFIG.description,
|
||||
keywords: [
|
||||
'Enterprise Software Development',
|
||||
'Custom Software Development',
|
||||
'Data Replication Services',
|
||||
'Incident Management SaaS',
|
||||
'AI Business Intelligence',
|
||||
'Backend Engineering',
|
||||
'Frontend Engineering',
|
||||
'Systems Integration',
|
||||
'External Systems Integration',
|
||||
'Payment Terminal Integration',
|
||||
'ERP Integration',
|
||||
'Cloud Platform Integration',
|
||||
'Fiscal Printer Integration',
|
||||
'Enterprise Technology Solutions',
|
||||
'Digital Transformation',
|
||||
'Software Consulting',
|
||||
'API Development',
|
||||
'Microservices Architecture',
|
||||
'Cloud Migration',
|
||||
'DevOps Services',
|
||||
],
|
||||
};
|
||||
|
||||
// Generate metadata for pages
|
||||
interface PageMetadataProps {
|
||||
title?: string;
|
||||
description?: string;
|
||||
keywords?: string[];
|
||||
image?: string;
|
||||
url?: string;
|
||||
type?: 'website' | 'article' | 'product' | 'service';
|
||||
publishedTime?: string;
|
||||
modifiedTime?: string;
|
||||
author?: string;
|
||||
noindex?: boolean;
|
||||
nofollow?: boolean;
|
||||
}
|
||||
|
||||
export function generateMetadata({
|
||||
title,
|
||||
description,
|
||||
keywords = [],
|
||||
image,
|
||||
url,
|
||||
type = 'website',
|
||||
publishedTime,
|
||||
modifiedTime,
|
||||
author,
|
||||
noindex = false,
|
||||
nofollow = false,
|
||||
}: PageMetadataProps = {}): Metadata {
|
||||
const pageTitle = title
|
||||
? `${title} | ${SITE_CONFIG.name}`
|
||||
: DEFAULT_SEO.title;
|
||||
const pageDescription = description || DEFAULT_SEO.description;
|
||||
const pageImage = image
|
||||
? `${SITE_CONFIG.url}${image}`
|
||||
: `${SITE_CONFIG.url}${SITE_CONFIG.ogImage}`;
|
||||
const pageUrl = url ? `${SITE_CONFIG.url}${url}` : SITE_CONFIG.url;
|
||||
const allKeywords = [...DEFAULT_SEO.keywords, ...keywords];
|
||||
|
||||
const metadata: Metadata = {
|
||||
title: pageTitle,
|
||||
description: pageDescription,
|
||||
keywords: allKeywords,
|
||||
authors: [
|
||||
{
|
||||
name: author || SITE_CONFIG.name,
|
||||
url: SITE_CONFIG.url,
|
||||
},
|
||||
],
|
||||
creator: SITE_CONFIG.name,
|
||||
publisher: SITE_CONFIG.name,
|
||||
icons: {
|
||||
icon: '/images/logo-light.png',
|
||||
shortcut: '/images/logo-light.png',
|
||||
apple: '/images/logo-light.png',
|
||||
},
|
||||
formatDetection: {
|
||||
email: false,
|
||||
address: false,
|
||||
telephone: false,
|
||||
},
|
||||
metadataBase: new URL(SITE_CONFIG.url),
|
||||
alternates: {
|
||||
canonical: pageUrl,
|
||||
},
|
||||
openGraph: {
|
||||
type: type === 'article' ? 'article' : 'website',
|
||||
locale: 'en_US',
|
||||
url: pageUrl,
|
||||
siteName: SITE_CONFIG.name,
|
||||
title: pageTitle,
|
||||
description: pageDescription,
|
||||
images: [
|
||||
{
|
||||
url: pageImage,
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: title || SITE_CONFIG.name,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: pageTitle,
|
||||
description: pageDescription,
|
||||
images: [pageImage],
|
||||
},
|
||||
robots: {
|
||||
index: !noindex,
|
||||
follow: !nofollow,
|
||||
googleBot: {
|
||||
index: !noindex,
|
||||
follow: !nofollow,
|
||||
'max-video-preview': -1,
|
||||
'max-image-preview': 'large',
|
||||
'max-snippet': -1,
|
||||
},
|
||||
},
|
||||
verification: {
|
||||
google: process.env.NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION,
|
||||
yandex: process.env.NEXT_PUBLIC_YANDEX_VERIFICATION,
|
||||
},
|
||||
};
|
||||
|
||||
// Add article-specific metadata
|
||||
if (type === 'article' && (publishedTime || modifiedTime)) {
|
||||
metadata.openGraph = {
|
||||
...metadata.openGraph,
|
||||
type: 'article',
|
||||
publishedTime,
|
||||
modifiedTime,
|
||||
authors: [author || SITE_CONFIG.name],
|
||||
};
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
// Service-specific metadata generator
|
||||
export function generateServiceMetadata(service: {
|
||||
title: string;
|
||||
description: string;
|
||||
short_description?: string;
|
||||
slug: string;
|
||||
category?: { name: string };
|
||||
technologies?: string;
|
||||
duration?: string;
|
||||
}) {
|
||||
const keywords = [
|
||||
service.title,
|
||||
`${service.title} Services`,
|
||||
service.category?.name || 'Enterprise Services',
|
||||
'Custom Development',
|
||||
'Enterprise Solutions',
|
||||
];
|
||||
|
||||
if (service.technologies) {
|
||||
const techs = service.technologies.split(',').map((t) => t.trim());
|
||||
keywords.push(...techs);
|
||||
}
|
||||
|
||||
return generateMetadata({
|
||||
title: service.title,
|
||||
description: service.short_description || service.description,
|
||||
keywords,
|
||||
url: `/services/${service.slug}`,
|
||||
type: 'service' as any,
|
||||
});
|
||||
}
|
||||
|
||||
// Blog-specific metadata generator
|
||||
export function generateBlogMetadata(post: {
|
||||
title: string;
|
||||
description?: string;
|
||||
excerpt?: string;
|
||||
slug: string;
|
||||
image?: string;
|
||||
published_at?: string;
|
||||
updated_at?: string;
|
||||
author?: { name: string };
|
||||
category?: { name: string };
|
||||
tags?: string[];
|
||||
}) {
|
||||
const keywords = [
|
||||
post.category?.name || 'Technology',
|
||||
'Blog',
|
||||
'Tech Insights',
|
||||
...(post.tags || []),
|
||||
];
|
||||
|
||||
return generateMetadata({
|
||||
title: post.title,
|
||||
description: post.description || post.excerpt,
|
||||
keywords,
|
||||
image: post.image,
|
||||
url: `/insights/${post.slug}`,
|
||||
type: 'article',
|
||||
publishedTime: post.published_at,
|
||||
modifiedTime: post.updated_at,
|
||||
author: post.author?.name,
|
||||
});
|
||||
}
|
||||
|
||||
// Case Study metadata generator
|
||||
export function generateCaseStudyMetadata(caseStudy: {
|
||||
title: string;
|
||||
description?: string;
|
||||
excerpt?: string;
|
||||
slug: string;
|
||||
image?: string;
|
||||
client_name?: string;
|
||||
industry?: string;
|
||||
technologies?: string;
|
||||
}) {
|
||||
const keywords = [
|
||||
'Case Study',
|
||||
caseStudy.client_name || '',
|
||||
caseStudy.industry || '',
|
||||
'Success Story',
|
||||
'Client Project',
|
||||
];
|
||||
|
||||
if (caseStudy.technologies) {
|
||||
const techs = caseStudy.technologies.split(',').map((t) => t.trim());
|
||||
keywords.push(...techs);
|
||||
}
|
||||
|
||||
return generateMetadata({
|
||||
title: caseStudy.title,
|
||||
description: caseStudy.description || caseStudy.excerpt,
|
||||
keywords: keywords.filter(Boolean),
|
||||
image: caseStudy.image,
|
||||
url: `/case-study/${caseStudy.slug}`,
|
||||
type: 'article',
|
||||
});
|
||||
}
|
||||
|
||||
// Career metadata generator
|
||||
export function generateCareerMetadata(job: {
|
||||
title: string;
|
||||
description?: string;
|
||||
slug: string;
|
||||
location?: string;
|
||||
department?: string;
|
||||
employment_type?: string;
|
||||
}) {
|
||||
const keywords = [
|
||||
job.title,
|
||||
'Career',
|
||||
'Job Opening',
|
||||
job.department || '',
|
||||
job.location || '',
|
||||
job.employment_type || '',
|
||||
'Join Our Team',
|
||||
].filter(Boolean);
|
||||
|
||||
return generateMetadata({
|
||||
title: `${job.title} - Careers`,
|
||||
description: job.description,
|
||||
keywords,
|
||||
url: `/career/${job.slug}`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -33,11 +33,133 @@ const nextConfig = {
|
||||
// pathname: '/media/**',
|
||||
// },
|
||||
],
|
||||
formats: ['image/avif', 'image/webp'],
|
||||
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
||||
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
|
||||
minimumCacheTTL: 60,
|
||||
},
|
||||
sassOptions: {
|
||||
includePaths: ['./public/styles', './node_modules'],
|
||||
quietDeps: true, // Suppress deprecation warnings from dependencies
|
||||
},
|
||||
// Compiler optimizations
|
||||
compiler: {
|
||||
removeConsole: process.env.NODE_ENV === 'production' ? {
|
||||
exclude: ['error', 'warn'],
|
||||
} : false,
|
||||
},
|
||||
// Compression
|
||||
compress: true,
|
||||
// Production optimizations
|
||||
productionBrowserSourceMaps: false,
|
||||
// Performance optimizations (swcMinify removed - default in Next.js 15)
|
||||
// Enterprise Security Headers
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/:path*',
|
||||
headers: [
|
||||
// Security Headers
|
||||
{
|
||||
key: 'X-DNS-Prefetch-Control',
|
||||
value: 'on'
|
||||
},
|
||||
{
|
||||
key: 'Strict-Transport-Security',
|
||||
value: 'max-age=63072000; includeSubDomains; preload'
|
||||
},
|
||||
{
|
||||
key: 'X-Frame-Options',
|
||||
value: 'SAMEORIGIN'
|
||||
},
|
||||
{
|
||||
key: 'X-Content-Type-Options',
|
||||
value: 'nosniff'
|
||||
},
|
||||
{
|
||||
key: 'X-XSS-Protection',
|
||||
value: '1; mode=block'
|
||||
},
|
||||
{
|
||||
key: 'Referrer-Policy',
|
||||
value: 'strict-origin-when-cross-origin'
|
||||
},
|
||||
{
|
||||
key: 'Permissions-Policy',
|
||||
value: 'camera=(), microphone=(), geolocation=(), interest-cohort=()'
|
||||
},
|
||||
{
|
||||
key: 'Content-Security-Policy',
|
||||
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'; img-src 'self' data: https: http://localhost:8000 http://localhost:8080; font-src 'self' data:; 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'"
|
||||
},
|
||||
// Performance Headers
|
||||
{
|
||||
key: 'Cache-Control',
|
||||
value: 'public, max-age=31536000, immutable'
|
||||
},
|
||||
],
|
||||
},
|
||||
// Static assets caching
|
||||
{
|
||||
source: '/images/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'Cache-Control',
|
||||
value: 'public, max-age=31536000, immutable',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/icons/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'Cache-Control',
|
||||
value: 'public, max-age=31536000, immutable',
|
||||
},
|
||||
],
|
||||
},
|
||||
// API responses - no cache for dynamic content
|
||||
{
|
||||
source: '/api/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'Cache-Control',
|
||||
value: 'no-store, must-revalidate',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
// Redirects for SEO
|
||||
async redirects() {
|
||||
return [
|
||||
// Redirect trailing slashes
|
||||
{
|
||||
source: '/:path+/',
|
||||
destination: '/:path+',
|
||||
permanent: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
// Rewrites for API proxy (Production: routes /api to backend through nginx)
|
||||
async rewrites() {
|
||||
// In development, proxy to Django backend
|
||||
// In production, nginx handles this
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return [
|
||||
{
|
||||
source: '/api/:path*',
|
||||
destination: `${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'}/api/:path*`,
|
||||
},
|
||||
{
|
||||
source: '/media/:path*',
|
||||
destination: `${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'}/media/:path*`,
|
||||
},
|
||||
]
|
||||
}
|
||||
// In production, these are handled by nginx reverse proxy
|
||||
return []
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
||||
@@ -99,12 +99,22 @@ export const OffcanvasData = [
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
title: "Contact Us",
|
||||
path: "/contact-us",
|
||||
title: "Support Center",
|
||||
path: "/support-center",
|
||||
parent_id: null,
|
||||
display_order: 7,
|
||||
submenu: null,
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
updated_at: "2024-01-01T00:00:00Z"
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
title: "Contact Us",
|
||||
path: "/contact-us",
|
||||
parent_id: null,
|
||||
display_order: 8,
|
||||
submenu: null,
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
updated_at: "2024-01-01T00:00:00Z"
|
||||
}
|
||||
];
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
// modern CSS framework (Bootstrap replacement)
|
||||
@use "./utilities/modern-framework";
|
||||
@use "./utilities/content-protection";
|
||||
|
||||
/* ==============
|
||||
========= css table of contents =========
|
||||
|
||||
123
gnx-react/public/styles/utilities/_content-protection.scss
Normal file
123
gnx-react/public/styles/utilities/_content-protection.scss
Normal file
@@ -0,0 +1,123 @@
|
||||
// ============================================================================
|
||||
// Content Protection Styles
|
||||
// ============================================================================
|
||||
// Prevents content copying, text selection, and image downloading
|
||||
|
||||
// Disable text selection globally
|
||||
.content-protected {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
// Prevent tap highlight on mobile
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
// Disable image dragging
|
||||
img {
|
||||
-webkit-user-drag: none;
|
||||
-khtml-user-drag: none;
|
||||
-moz-user-drag: none;
|
||||
-o-user-drag: none;
|
||||
user-drag: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Protect all text content
|
||||
* {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow selection for input fields and textareas
|
||||
.content-protected input,
|
||||
.content-protected textarea,
|
||||
.content-protected [contenteditable="true"] {
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
// Image protection with overlay
|
||||
.protected-image-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
-webkit-user-drag: none;
|
||||
-khtml-user-drag: none;
|
||||
-moz-user-drag: none;
|
||||
-o-user-drag: none;
|
||||
user-drag: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Transparent overlay to prevent right-click on images
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
pointer-events: auto;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Watermark overlay (optional - can be enabled per image)
|
||||
.watermarked-image {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: 'GNX Soft';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
color: rgba(255, 255, 255, 0.15);
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
// Hide on print (prevent print screen)
|
||||
@media print {
|
||||
.no-print {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body::after {
|
||||
content: 'This content is protected and cannot be printed. Visit gnxsoft.com';
|
||||
display: block;
|
||||
text-align: center;
|
||||
padding: 50px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// Additional protection for code blocks
|
||||
.content-protected pre,
|
||||
.content-protected code {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
// Blur content when dev tools are open (optional)
|
||||
.devtools-open {
|
||||
filter: blur(5px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ djangorestframework==3.14.0
|
||||
django-cors-headers==4.3.1
|
||||
python-decouple==3.8
|
||||
Pillow==10.1.0
|
||||
psycopg2-binary==2.9.9
|
||||
celery==5.3.4
|
||||
redis==5.0.1
|
||||
django-environ==0.11.2
|
||||
|
||||
195
nginx-gnxsoft.conf
Normal file
195
nginx-gnxsoft.conf
Normal file
@@ -0,0 +1,195 @@
|
||||
# Production Nginx Configuration for GNX Soft
|
||||
# Place this in /etc/nginx/sites-available/gnxsoft
|
||||
#
|
||||
# DEPLOYMENT NOTES:
|
||||
# 1. Frontend: Next.js production build runs on port 3000
|
||||
# - Build: npm run build
|
||||
# - Start: npm start (or use PM2: pm2 start npm --name "gnxsoft-frontend" -- start)
|
||||
# 2. Backend: Django runs on port 8000 (internal only)
|
||||
# - Use Gunicorn: gunicorn gnx.wsgi:application --bind 127.0.0.1:8000
|
||||
# - Or PM2: pm2 start gunicorn --name "gnxsoft-backend" -- gnx.wsgi:application --bind 127.0.0.1:8000
|
||||
|
||||
# Frontend - Public facing (Next.js Production Server)
|
||||
upstream frontend {
|
||||
server 127.0.0.1:3000;
|
||||
keepalive 64;
|
||||
}
|
||||
|
||||
# Backend - Internal only (Django)
|
||||
upstream backend_internal {
|
||||
server 127.0.0.1:8000;
|
||||
keepalive 64;
|
||||
}
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name gnxsoft.com www.gnxsoft.com;
|
||||
|
||||
# Let's Encrypt validation
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# Redirect all other traffic to HTTPS
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS Frontend
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name gnxsoft.com www.gnxsoft.com;
|
||||
|
||||
# SSL Configuration
|
||||
ssl_certificate /etc/letsencrypt/live/gnxsoft.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/gnxsoft.com/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# Security Headers
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
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;
|
||||
limit_req_status 429;
|
||||
|
||||
# Client settings
|
||||
client_max_body_size 10M;
|
||||
client_body_timeout 30s;
|
||||
client_header_timeout 30s;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/gnxsoft_access.log;
|
||||
error_log /var/log/nginx/gnxsoft_error.log warn;
|
||||
|
||||
# Root location - Frontend (Next.js)
|
||||
location / {
|
||||
limit_req zone=general_limit burst=50 nodelay;
|
||||
|
||||
proxy_pass http://frontend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
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_cache_bypass $http_upgrade;
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# API Proxy - Frontend talks to backend ONLY through this internal proxy
|
||||
# Backend port 8000 is BLOCKED from internet by firewall
|
||||
location /api/ {
|
||||
limit_req zone=api_limit burst=20 nodelay;
|
||||
|
||||
# Internal proxy to backend (127.0.0.1:8000)
|
||||
# Backend is NOT accessible from public internet
|
||||
proxy_pass http://backend_internal/api/;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# Backend sees request as coming from localhost
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP 127.0.0.1;
|
||||
proxy_set_header X-Forwarded-For 127.0.0.1;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# 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" 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/gnxsoft/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/gnxsoft/static/;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Next.js static files
|
||||
location /_next/static/ {
|
||||
proxy_pass http://frontend;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Deny access to hidden files
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
# Deny access to backend admin (extra security)
|
||||
location /admin {
|
||||
deny all;
|
||||
return 404;
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "OK\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# IMPORTANT SECURITY NOTES:
|
||||
# ==============================================================================
|
||||
# 1. Backend runs on 127.0.0.1:8000 (internal only)
|
||||
# 2. Firewall BLOCKS external access to port 8000
|
||||
# 3. Only nginx can reach backend (internal network)
|
||||
# 4. Public internet can ONLY access nginx (ports 80, 443)
|
||||
# 5. All API calls go through nginx proxy (/api/* → 127.0.0.1:8000/api/*)
|
||||
# 6. Backend IP whitelist middleware ensures only localhost requests
|
||||
# ==============================================================================
|
||||
|
||||
Reference in New Issue
Block a user