mirror of
https://github.com/crivion/laranode.git
synced 2026-05-06 21:52:34 +08:00
ssl with letsencrypt for websites
This commit is contained in:
@@ -12,6 +12,7 @@ use App\Services\Websites\UpdateWebsitePHPVersionService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class WebsiteController extends Controller
|
||||
@@ -80,4 +81,143 @@ class WebsiteController extends Controller
|
||||
|
||||
return redirect()->route('websites.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle SSL certificate for a website
|
||||
*/
|
||||
public function toggleSsl(Request $request, Website $website)
|
||||
{
|
||||
Gate::authorize('update', $website);
|
||||
|
||||
$request->validate([
|
||||
'enabled' => 'required|boolean',
|
||||
'email' => 'required_if:enabled,true|email'
|
||||
]);
|
||||
|
||||
try {
|
||||
if ($request->enabled) {
|
||||
// Generate SSL certificate
|
||||
$this->generateSslCertificate($website, $request->email);
|
||||
} else {
|
||||
// Remove SSL certificate
|
||||
$this->removeSslCertificate($website);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => $request->enabled ? 'SSL certificate generated successfully' : 'SSL certificate removed successfully',
|
||||
'ssl_status' => $website->fresh()->ssl_status,
|
||||
'ssl_enabled' => $website->fresh()->ssl_enabled
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Failed to ' . ($request->enabled ? 'generate' : 'remove') . ' SSL certificate: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate SSL certificate for a website
|
||||
*/
|
||||
private function generateSslCertificate(Website $website, string $email): void
|
||||
{
|
||||
// Update status to pending
|
||||
$website->update([
|
||||
'ssl_status' => 'pending',
|
||||
'ssl_enabled' => true
|
||||
]);
|
||||
|
||||
// Run SSL generation script
|
||||
$scriptPath = base_path('laranode-scripts/bin/laranode-ssl-manager.sh');
|
||||
$result = Process::run([
|
||||
'bash', $scriptPath, 'generate',
|
||||
$website->url,
|
||||
$email,
|
||||
$website->fullDocumentRoot
|
||||
]);
|
||||
|
||||
if ($result->failed()) {
|
||||
$website->update([
|
||||
'ssl_status' => 'inactive',
|
||||
'ssl_enabled' => false
|
||||
]);
|
||||
throw new \Exception($result->errorOutput());
|
||||
}
|
||||
|
||||
// Check SSL status
|
||||
$statusResult = Process::run([
|
||||
'bash', $scriptPath, 'status', $website->url
|
||||
]);
|
||||
|
||||
$sslStatus = trim($statusResult->output());
|
||||
|
||||
// Update website with SSL information
|
||||
$website->update([
|
||||
'ssl_status' => $sslStatus === 'active' ? 'active' : 'inactive',
|
||||
'ssl_generated_at' => now(),
|
||||
'ssl_expires_at' => $sslStatus === 'active' ? now()->addDays(90) : null
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove SSL certificate for a website
|
||||
*/
|
||||
private function removeSslCertificate(Website $website): void
|
||||
{
|
||||
// Run SSL removal script
|
||||
$scriptPath = base_path('laranode-scripts/bin/laranode-ssl-manager.sh');
|
||||
$result = Process::run([
|
||||
'bash', $scriptPath, 'remove', $website->url
|
||||
]);
|
||||
|
||||
if ($result->failed()) {
|
||||
throw new \Exception($result->errorOutput());
|
||||
}
|
||||
|
||||
// Update website SSL status
|
||||
$website->update([
|
||||
'ssl_enabled' => false,
|
||||
'ssl_status' => 'inactive',
|
||||
'ssl_expires_at' => null,
|
||||
'ssl_generated_at' => null
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check SSL status for a website
|
||||
*/
|
||||
public function checkSslStatus(Website $website)
|
||||
{
|
||||
Gate::authorize('view', $website);
|
||||
|
||||
try {
|
||||
$scriptPath = base_path('laranode-scripts/bin/laranode-ssl-manager.sh');
|
||||
$result = Process::run([
|
||||
'bash', $scriptPath, 'status', $website->url
|
||||
]);
|
||||
|
||||
$sslStatus = trim($result->output());
|
||||
|
||||
// Update website SSL status
|
||||
$website->update([
|
||||
'ssl_status' => $sslStatus,
|
||||
'ssl_enabled' => $sslStatus === 'active'
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'ssl_status' => $sslStatus,
|
||||
'ssl_enabled' => $sslStatus === 'active',
|
||||
'status_text' => $website->getSslStatusText()
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Failed to check SSL status: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,29 +7,38 @@ use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Website extends Model
|
||||
{
|
||||
public $appends = ['fullDocumentRoot'];
|
||||
protected $casts = [
|
||||
'ssl_enabled' => 'boolean',
|
||||
'ssl_expires_at' => 'datetime',
|
||||
'ssl_generated_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'url',
|
||||
'document_root',
|
||||
'website_root',
|
||||
'php_version_id',
|
||||
'ssl_enabled',
|
||||
'ssl_status',
|
||||
'ssl_expires_at',
|
||||
'ssl_generated_at',
|
||||
];
|
||||
|
||||
public function getWebsiteRootAttribute(): string
|
||||
{
|
||||
return $this->user->homedir . '/domains/' . $this->url;
|
||||
return $this->user?->homedir . '/domains/' . $this->url;
|
||||
}
|
||||
|
||||
// not using casts as it's not working in some scenarios
|
||||
public function getFullDocumentRootAttribute(): string
|
||||
{
|
||||
return $this->user->homedir . '/domains/' . $this->url . $this->document_root;
|
||||
return $this->user?->homedir . '/domains/' . $this->url . $this->document_root;
|
||||
}
|
||||
|
||||
public function scopeMine(Builder $query): Builder
|
||||
{
|
||||
return $query->when(!auth()->user()->isAdmin(), fn($query) => $query->where('user_id', auth()->id()));
|
||||
$user = auth()->user();
|
||||
return $query->when($user && !$user->isAdmin(), fn($query) => $query->where('user_id', $user->id));
|
||||
}
|
||||
|
||||
public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
@@ -41,4 +50,47 @@ class Website extends Model
|
||||
{
|
||||
return $this->belongsTo(PhpVersion::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if SSL certificate is active and valid
|
||||
*/
|
||||
public function isSslActive(): bool
|
||||
{
|
||||
return $this->ssl_enabled && $this->ssl_status === 'active';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if SSL certificate is expired
|
||||
*/
|
||||
public function isSslExpired(): bool
|
||||
{
|
||||
return $this->ssl_status === 'expired' ||
|
||||
($this->ssl_expires_at && $this->ssl_expires_at->isPast());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SSL status display text
|
||||
*/
|
||||
public function getSslStatusText(): string
|
||||
{
|
||||
return match($this->ssl_status) {
|
||||
'active' => 'SSL Active',
|
||||
'expired' => 'SSL Expired',
|
||||
'pending' => 'SSL Pending',
|
||||
default => 'SSL Inactive'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SSL status color class for frontend
|
||||
*/
|
||||
public function getSslStatusColor(): string
|
||||
{
|
||||
return match($this->ssl_status) {
|
||||
'active' => 'text-green-600',
|
||||
'expired' => 'text-red-600',
|
||||
'pending' => 'text-yellow-600',
|
||||
default => 'text-gray-500'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('websites', function (Blueprint $table) {
|
||||
$table->boolean('ssl_enabled')->default(false);
|
||||
$table->string('ssl_status')->default('inactive'); // inactive, active, expired, pending
|
||||
$table->timestamp('ssl_expires_at')->nullable();
|
||||
$table->timestamp('ssl_generated_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('websites', function (Blueprint $table) {
|
||||
$table->dropColumn(['ssl_enabled', 'ssl_status', 'ssl_expires_at', 'ssl_generated_at']);
|
||||
});
|
||||
}
|
||||
};
|
||||
259
laranode-scripts/bin/laranode-ssl-manager.sh
Executable file
259
laranode-scripts/bin/laranode-ssl-manager.sh
Executable file
@@ -0,0 +1,259 @@
|
||||
#!/bin/bash
|
||||
|
||||
# SSL Certificate Manager for Laranode
|
||||
# This script handles SSL certificate generation and management using Let's Encrypt
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
WEBROOT_PATH="/var/www/html"
|
||||
CERTBOT_PATH="/usr/bin/certbot"
|
||||
APACHE_SITES_PATH="/etc/apache2/sites-available"
|
||||
APACHE_ENABLED_PATH="/etc/apache2/sites-enabled"
|
||||
SSL_CERTS_PATH="/etc/letsencrypt/live"
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Function to check if certbot is installed
|
||||
check_certbot() {
|
||||
if ! command -v certbot &> /dev/null; then
|
||||
print_error "Certbot is not installed. Please install it first:"
|
||||
echo "sudo apt update && sudo apt install certbot python3-certbot-apache"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check if domain is accessible
|
||||
check_domain_accessibility() {
|
||||
local domain=$1
|
||||
print_status "Checking if domain $domain is accessible..."
|
||||
|
||||
if ! curl -s --connect-timeout 10 "http://$domain" > /dev/null; then
|
||||
print_error "Domain $domain is not accessible. Please ensure:"
|
||||
echo "1. Domain DNS points to this server"
|
||||
echo "2. Apache virtual host is configured and enabled"
|
||||
echo "3. Domain is accessible via HTTP"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_status "Domain $domain is accessible"
|
||||
}
|
||||
|
||||
# Function to generate SSL certificate
|
||||
generate_ssl_certificate() {
|
||||
local domain=$1
|
||||
local email=$2
|
||||
|
||||
print_status "Generating SSL certificate for $domain..."
|
||||
|
||||
# Check if certificate already exists
|
||||
if [ -d "$SSL_CERTS_PATH/$domain" ]; then
|
||||
print_warning "SSL certificate for $domain already exists"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Generate certificate using certbot
|
||||
if certbot certonly \
|
||||
--webroot \
|
||||
--webroot-path="$WEBROOT_PATH" \
|
||||
--email "$email" \
|
||||
--agree-tos \
|
||||
--no-eff-email \
|
||||
--domains "$domain" \
|
||||
--non-interactive; then
|
||||
print_status "SSL certificate generated successfully for $domain"
|
||||
return 0
|
||||
else
|
||||
print_error "Failed to generate SSL certificate for $domain"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to create SSL-enabled Apache virtual host
|
||||
create_ssl_vhost() {
|
||||
local domain=$1
|
||||
local document_root=$2
|
||||
|
||||
print_status "Creating SSL-enabled virtual host for $domain..."
|
||||
|
||||
local vhost_file="$APACHE_SITES_PATH/$domain-ssl.conf"
|
||||
|
||||
cat > "$vhost_file" << EOF
|
||||
<VirtualHost *:443>
|
||||
ServerName $domain
|
||||
DocumentRoot $document_root
|
||||
|
||||
SSLEngine on
|
||||
SSLCertificateFile $SSL_CERTS_PATH/$domain/fullchain.pem
|
||||
SSLCertificateKeyFile $SSL_CERTS_PATH/$domain/privkey.pem
|
||||
|
||||
# Include SSL configuration
|
||||
Include /etc/apache2/conf-available/ssl-params.conf
|
||||
|
||||
# Security headers
|
||||
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
Header always set X-Content-Type-Options nosniff
|
||||
Header always set X-Frame-Options DENY
|
||||
Header always set X-XSS-Protection "1; mode=block"
|
||||
|
||||
# Logging
|
||||
ErrorLog \${APACHE_LOG_DIR}/$domain-ssl_error.log
|
||||
CustomLog \${APACHE_LOG_DIR}/$domain-ssl_access.log combined
|
||||
</VirtualHost>
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
<VirtualHost *:80>
|
||||
ServerName $domain
|
||||
Redirect permanent / https://$domain/
|
||||
</VirtualHost>
|
||||
EOF
|
||||
|
||||
# Enable the site
|
||||
a2ensite "$domain-ssl.conf"
|
||||
|
||||
# Test Apache configuration
|
||||
if apache2ctl configtest; then
|
||||
systemctl reload apache2
|
||||
print_status "SSL virtual host created and enabled for $domain"
|
||||
return 0
|
||||
else
|
||||
print_error "Apache configuration test failed"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to remove SSL certificate
|
||||
remove_ssl_certificate() {
|
||||
local domain=$1
|
||||
|
||||
print_status "Removing SSL certificate for $domain..."
|
||||
|
||||
# Disable SSL site
|
||||
if [ -f "$APACHE_SITES_PATH/$domain-ssl.conf" ]; then
|
||||
a2dissite "$domain-ssl.conf"
|
||||
rm -f "$APACHE_SITES_PATH/$domain-ssl.conf"
|
||||
fi
|
||||
|
||||
# Remove certificate files
|
||||
if [ -d "$SSL_CERTS_PATH/$domain" ]; then
|
||||
certbot delete --cert-name "$domain" --non-interactive
|
||||
print_status "SSL certificate removed for $domain"
|
||||
else
|
||||
print_warning "No SSL certificate found for $domain"
|
||||
fi
|
||||
|
||||
# Reload Apache
|
||||
systemctl reload apache2
|
||||
print_status "SSL configuration removed for $domain"
|
||||
}
|
||||
|
||||
# Function to check SSL certificate status
|
||||
check_ssl_status() {
|
||||
local domain=$1
|
||||
|
||||
if [ -d "$SSL_CERTS_PATH/$domain" ]; then
|
||||
# Check if certificate is valid and not expired
|
||||
local cert_file="$SSL_CERTS_PATH/$domain/fullchain.pem"
|
||||
if [ -f "$cert_file" ]; then
|
||||
local expiry_date=$(openssl x509 -in "$cert_file" -noout -enddate | cut -d= -f2)
|
||||
local expiry_timestamp=$(date -d "$expiry_date" +%s)
|
||||
local current_timestamp=$(date +%s)
|
||||
|
||||
if [ $expiry_timestamp -gt $current_timestamp ]; then
|
||||
echo "active"
|
||||
return 0
|
||||
else
|
||||
echo "expired"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "inactive"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Function to renew SSL certificates
|
||||
renew_ssl_certificates() {
|
||||
print_status "Renewing SSL certificates..."
|
||||
|
||||
if certbot renew --quiet; then
|
||||
systemctl reload apache2
|
||||
print_status "SSL certificates renewed successfully"
|
||||
return 0
|
||||
else
|
||||
print_error "Failed to renew SSL certificates"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main script logic
|
||||
case "$1" in
|
||||
"generate")
|
||||
if [ $# -ne 3 ]; then
|
||||
echo "Usage: $0 generate <domain> <email>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
domain=$2
|
||||
email=$3
|
||||
|
||||
check_certbot
|
||||
check_domain_accessibility "$domain"
|
||||
generate_ssl_certificate "$domain" "$email"
|
||||
create_ssl_vhost "$domain" "$4"
|
||||
;;
|
||||
|
||||
"remove")
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 remove <domain>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
domain=$2
|
||||
remove_ssl_certificate "$domain"
|
||||
;;
|
||||
|
||||
"status")
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 status <domain>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
domain=$2
|
||||
status=$(check_ssl_status "$domain")
|
||||
echo "$status"
|
||||
;;
|
||||
|
||||
"renew")
|
||||
renew_ssl_certificates
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {generate|remove|status|renew}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " generate <domain> <email> [document_root] - Generate SSL certificate for domain"
|
||||
echo " remove <domain> - Remove SSL certificate for domain"
|
||||
echo " status <domain> - Check SSL certificate status"
|
||||
echo " renew - Renew all SSL certificates"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -1,4 +1,3 @@
|
||||
import { FaUsers } from "react-icons/fa6";
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
|
||||
import { Head, Link, usePage } from '@inertiajs/react';
|
||||
import ConfirmationButton from "@/Components/ConfirmationButton";
|
||||
@@ -6,8 +5,8 @@ import { TiDelete } from "react-icons/ti";
|
||||
import { toast } from "react-toastify";
|
||||
import { router } from '@inertiajs/react'
|
||||
import { TbWorldWww } from "react-icons/tb";
|
||||
import { FaDatabase, FaEdit } from "react-icons/fa";
|
||||
import { Tooltip } from 'react-tooltip'
|
||||
import { MdLock, MdLockOpen } from "react-icons/md";
|
||||
import { FaToggleOn, FaToggleOff } from "react-icons/fa";
|
||||
import CreateWebsiteForm from "./Partials/CreateWebsiteForm";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
@@ -40,6 +39,53 @@ export default function Websites({ websites, serverIp }) {
|
||||
});
|
||||
};
|
||||
|
||||
const toggleSsl = (website) => {
|
||||
const isEnabled = website.ssl_enabled;
|
||||
const action = isEnabled ? 'disable' : 'enable';
|
||||
|
||||
if (!isEnabled) {
|
||||
// If enabling SSL, prompt for email
|
||||
const email = prompt('Please enter your email address for SSL certificate generation:');
|
||||
if (!email) return;
|
||||
|
||||
// Basic email validation
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
toast.error('Please enter a valid email address');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const requestData = {
|
||||
enabled: !isEnabled,
|
||||
...(isEnabled ? {} : { email: email })
|
||||
};
|
||||
|
||||
fetch(route('websites.ssl.toggle', { website: website.id }), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(requestData)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
toast.success(data.message);
|
||||
// Refresh the page to show updated SSL status
|
||||
router.reload();
|
||||
} else {
|
||||
toast.error(data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
toast.error('Failed to toggle SSL certificate');
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthenticatedLayout
|
||||
header={
|
||||
@@ -61,6 +107,7 @@ export default function Websites({ websites, serverIp }) {
|
||||
<thead className="text-gray-700 uppercase bg-gray-200 dark:bg-gray-700 dark:text-gray-300 text-sm">
|
||||
<tr>
|
||||
<th className="px-6 py-3">URL</th>
|
||||
<th className="px-6 py-3">SSL Status</th>
|
||||
<th className="px-6 py-3">Document Root</th>
|
||||
<th className="px-6 py-3">PHP Version</th>
|
||||
{auth.user.role == 'admin' && (
|
||||
@@ -73,7 +120,30 @@ export default function Websites({ websites, serverIp }) {
|
||||
{websites?.map((website, index) => (
|
||||
<tr key={`website-${index}`} className="bg-white border-b text-gray-700 dark:text-gray-200 dark:bg-gray-850 dark:border-gray-700 border-gray-200">
|
||||
<td className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
{website.url}
|
||||
<Link href={`https://${website.url}`} target="_blank">
|
||||
<TbWorldWww className='w-4 h-4' /> {website.url}
|
||||
</Link>
|
||||
</td>
|
||||
|
||||
<td className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<div className={`inline-flex items-center px-3 py-1 rounded-md text-sm font-medium ${
|
||||
website.ssl_status === 'active'
|
||||
? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'
|
||||
: website.ssl_status === 'expired'
|
||||
? 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'
|
||||
: website.ssl_status === 'pending'
|
||||
? 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200'
|
||||
: 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200'
|
||||
}`}>
|
||||
{website.ssl_status === 'active' ? (
|
||||
<MdLock className="w-4 h-4 mr-1" />
|
||||
) : (
|
||||
<MdLockOpen className="w-4 h-4 mr-1" />
|
||||
)}
|
||||
{website.ssl_status === 'active' ? 'SSL Active' :
|
||||
website.ssl_status === 'expired' ? 'SSL Expired' :
|
||||
website.ssl_status === 'pending' ? 'SSL Pending' : 'SSL Inactive'}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<div className="text-xs inline-flex items-center px-3 rounded-md border border-gray-300 bg-gray-100 text-gray-500 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400">
|
||||
@@ -116,7 +186,21 @@ export default function Websites({ websites, serverIp }) {
|
||||
whitespace-nowrap dark:text-white">
|
||||
|
||||
<div className='flex items-center space-x-2'>
|
||||
{/* <EditAccountForm account={account} /> */}
|
||||
<button
|
||||
onClick={() => toggleSsl(website)}
|
||||
className={`p-2 rounded-lg transition-colors ${
|
||||
website.ssl_enabled
|
||||
? 'bg-green-100 hover:bg-green-200 text-green-600 dark:bg-green-900 dark:hover:bg-green-800 dark:text-green-300'
|
||||
: 'bg-gray-100 hover:bg-gray-200 text-gray-600 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-400'
|
||||
}`}
|
||||
title={website.ssl_enabled ? 'Disable SSL' : 'Enable SSL'}
|
||||
>
|
||||
{website.ssl_enabled ? (
|
||||
<FaToggleOn className='w-5 h-5' />
|
||||
) : (
|
||||
<FaToggleOff className='w-5 h-5' />
|
||||
)}
|
||||
</button>
|
||||
|
||||
<ConfirmationButton doAction={() => deleteWebsite(website.id)}>
|
||||
<TiDelete className='w-6 h-6 text-red-500' />
|
||||
|
||||
@@ -30,6 +30,8 @@ Route::get('/accounts/leave-impersonation', [AccountsController::class, 'leaveIm
|
||||
|
||||
// Websites [Admin | User]
|
||||
Route::resource('/websites', WebsiteController::class)->middleware(['auth'])->except(['create', 'edit', 'show']);
|
||||
Route::post('/websites/{website}/ssl/toggle', [WebsiteController::class, 'toggleSsl'])->middleware(['auth'])->name('websites.ssl.toggle');
|
||||
Route::get('/websites/{website}/ssl/status', [WebsiteController::class, 'checkSslStatus'])->middleware(['auth'])->name('websites.ssl.status');
|
||||
|
||||
// PHP FPM Pools [Admin | User]
|
||||
Route::get('/php/get-versions', [PHPManagerController::class, 'getVersions'])->middleware(['auth'])->name('php.get-versions');
|
||||
|
||||
Reference in New Issue
Block a user