Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8ac6ee515 | ||
|
|
2fce30eddc | ||
|
|
8c64cd5d93 | ||
|
|
f369732f6f | ||
|
|
5f42fe1ef0 | ||
|
|
54af40a26f | ||
|
|
8bd2ba5c91 | ||
|
|
aaa6d6ce46 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nixpanel/cpanel",
|
"name": "@nixpanel/cpanel",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Outlet, NavLink, useNavigate } from 'react-router-dom'
|
import { Outlet, NavLink, useNavigate } from 'react-router-dom'
|
||||||
import {
|
import {
|
||||||
LayoutDashboard, FolderOpen, Database, Mail, Lock,
|
LayoutDashboard, FolderOpen, Database, Mail, Lock,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { Clock, Plus, Trash2, RefreshCw, ToggleLeft, ToggleRight } from 'lucide-react'
|
import { Plus, Trash2, RefreshCw, ToggleLeft, ToggleRight } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|
||||||
interface CronJob { id: number; schedule: string; command: string; comment: string | null; enabled: boolean }
|
interface CronJob { id: number; schedule: string; command: string; comment: string | null; enabled: boolean }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { Folder, FileText, ChevronRight, Trash2, Pencil, Plus, Upload, RefreshCw, FolderPlus, Save, X } from 'lucide-react'
|
import { Folder, FileText, ChevronRight, Trash2, Pencil, RefreshCw, FolderPlus, Save, X } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|
||||||
interface FileEntry { name: string; type: 'file' | 'directory'; size: number; mtime: string; path: string }
|
interface FileEntry { name: string; type: 'file' | 'directory'; size: number; mtime: string; path: string }
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export default function Login() {
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p className="text-center text-[#64748b] text-xs mt-4">NixClient v0.2.0 · Port 2083</p>
|
<p className="text-center text-[#64748b] text-xs mt-4">NixClient v0.3.0 · Port 2083</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { Database, Mail, Globe, FolderOpen, Lock, Anchor, Clock, HardDrive } from 'lucide-react'
|
import { Database, Mail, Globe, Lock, Anchor, Clock } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
import { useAuth } from '../contexts/AuthContext'
|
import { useAuth } from '../contexts/AuthContext'
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ function UsageBar({ used, limit, label }: { used: number; limit: number; label:
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function CountCard({ icon: Icon, label, used, limit, to }: { icon: React.ComponentType<{ size?: number; className?: string }>; label: string; used: number; limit: number; to: string }) {
|
function CountCard({ icon: Icon, label, used, limit, to }: { icon: React.ComponentType<{ size?: number | string; className?: string }>; label: string; used: number; limit: number; to: string }) {
|
||||||
return (
|
return (
|
||||||
<a href={to} className="bg-[#111827] border border-[#1e2d45] rounded-xl p-4 hover:border-sky-500/30 transition-colors block">
|
<a href={to} className="bg-[#111827] border border-[#1e2d45] rounded-xl p-4 hover:border-sky-500/30 transition-colors block">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Lock, RefreshCw, RotateCcw } from 'lucide-react'
|
import { Lock, RefreshCw, RotateCcw } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ interface Stats {
|
|||||||
counts: Record<string, number>
|
counts: Record<string, number>
|
||||||
}
|
}
|
||||||
|
|
||||||
function Bar({ label, used, limit, icon: Icon }: { label: string; used: number; limit: number; icon: React.ComponentType<{ size?: number; className?: string }> }) {
|
function Bar({ label, used, limit, icon: Icon }: { label: string; used: number; limit: number; icon: React.ComponentType<{ size?: number | string; className?: string }> }) {
|
||||||
const pct = limit === 0 ? 0 : Math.min(100, Math.round((used / limit) * 100))
|
const pct = limit === 0 ? 0 : Math.min(100, Math.round((used / limit) * 100))
|
||||||
const color = pct > 90 ? 'bg-red-500' : pct > 70 ? 'bg-amber-500' : 'bg-sky-500'
|
const color = pct > 90 ? 'bg-red-500' : pct > 70 ? 'bg-amber-500' : 'bg-sky-500'
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nixpanel/whm",
|
"name": "@nixpanel/whm",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Outlet, NavLink, useNavigate } from 'react-router-dom'
|
import { Outlet, NavLink, useNavigate } from 'react-router-dom'
|
||||||
import {
|
import {
|
||||||
LayoutDashboard, Users, Package, UserCog, Globe, Mail, Shield,
|
LayoutDashboard, Users, Package, UserCog, Globe, Mail, Shield,
|
||||||
Server, Database, HardDrive, Settings, LogOut, Menu, X,
|
Server, HardDrive, Settings, LogOut, Menu, X,
|
||||||
Activity, Lock, BarChart3, Cpu
|
Activity, Lock, BarChart3, Cpu
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { useAuth } from '../contexts/AuthContext'
|
import { useAuth } from '../contexts/AuthContext'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState, useCallback } from 'react'
|
import { useEffect, useState, useCallback } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Plus, Search, MoreVertical, UserX, UserCheck, Trash2, RefreshCw } from 'lucide-react'
|
import { Plus, Search, MoreVertical, UserX, UserCheck, Trash2, RefreshCw } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { HardDrive, Download, Trash2, RefreshCw, Play } from 'lucide-react'
|
import { HardDrive, Trash2, RefreshCw, Play } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|
||||||
interface Backup { path: string; filename: string; size: number; createdAt: string }
|
interface Backup { path: string; filename: string; size: number; createdAt: string }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { RefreshCw, Globe, Plus, Trash2, Pencil } from 'lucide-react'
|
import { RefreshCw, Globe, Plus, Trash2 } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|
||||||
interface Zone { id: number; domain: string; status: string; account: { username: string }; _count: { records: number } }
|
interface Zone { id: number; domain: string; status: string; account: { username: string }; _count: { records: number } }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState, useRef } from 'react'
|
import React, { useEffect, useState, useRef } from 'react'
|
||||||
import { Users, Globe, Server, HardDrive, Activity, AlertTriangle } from 'lucide-react'
|
import { Users, Server, HardDrive, Activity } from 'lucide-react'
|
||||||
import { AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'
|
import { AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ interface StatCard {
|
|||||||
label: string
|
label: string
|
||||||
value: string | number
|
value: string | number
|
||||||
sub?: string
|
sub?: string
|
||||||
icon: React.ComponentType<{ size?: number; className?: string }>
|
icon: React.ComponentType<{ size?: number | string; className?: string }>
|
||||||
color: string
|
color: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Mail, Plus, Trash2, RefreshCw } from 'lucide-react'
|
import { Plus, Trash2, RefreshCw } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|
||||||
interface EmailAccount { id: number; address: string; domain: string; quotaMb: number; usedMb: number; status: string }
|
interface EmailAccount { id: number; address: string; domain: string; quotaMb: number; usedMb: number; status: string }
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export default function Login() {
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p className="text-center text-[#64748b] text-xs mt-4">
|
<p className="text-center text-[#64748b] text-xs mt-4">
|
||||||
NixServer v0.2.0 · Port 2087
|
NixServer v0.3.0 · Port 2087
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Cpu, CheckCircle, XCircle, RefreshCw } from 'lucide-react'
|
import { Cpu, CheckCircle, XCircle, RefreshCw } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { Shield, Plus, Trash2, CheckCircle } from 'lucide-react'
|
import { Plus, Trash2, CheckCircle } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|
||||||
interface BlockedIp { id: number; ip: string; reason: string | null; createdAt: string }
|
interface BlockedIp { id: number; ip: string; reason: string | null; createdAt: string }
|
||||||
@@ -16,10 +16,7 @@ export default function Security() {
|
|||||||
const [events, setEvents] = useState<SecurityEvent[]>([])
|
const [events, setEvents] = useState<SecurityEvent[]>([])
|
||||||
const [newIp, setNewIp] = useState('')
|
const [newIp, setNewIp] = useState('')
|
||||||
const [reason, setReason] = useState('')
|
const [reason, setReason] = useState('')
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
|
|
||||||
const fetch = async () => {
|
const fetch = async () => {
|
||||||
setLoading(true)
|
|
||||||
try {
|
try {
|
||||||
const [b, e] = await Promise.all([
|
const [b, e] = await Promise.all([
|
||||||
api.get('/nixserver/security/blocked-ips'),
|
api.get('/nixserver/security/blocked-ips'),
|
||||||
@@ -27,7 +24,7 @@ export default function Security() {
|
|||||||
])
|
])
|
||||||
setBlocked(b.data.data)
|
setBlocked(b.data.data)
|
||||||
setEvents(e.data.data.items ?? e.data.data)
|
setEvents(e.data.data.items ?? e.data.data)
|
||||||
} finally { setLoading(false) }
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => { fetch() }, [])
|
useEffect(() => { fetch() }, [])
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Play, Square, RefreshCw, AlertCircle, CheckCircle, MinusCircle } from 'lucide-react'
|
import { RefreshCw, AlertCircle, CheckCircle, MinusCircle } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|
||||||
interface Service { name: string; status: 'running' | 'stopped' | 'failed' | 'unknown'; enabled: boolean }
|
interface Service { name: string; status: 'running' | 'stopped' | 'failed' | 'unknown'; enabled: boolean }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { RefreshCw } from 'lucide-react'
|
import { RefreshCw } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Server, RefreshCw, RotateCcw, CheckCircle, XCircle } from 'lucide-react'
|
import { Server, RefreshCw, RotateCcw, CheckCircle, XCircle } from 'lucide-react'
|
||||||
import { api } from '../api/client'
|
import { api } from '../api/client'
|
||||||
|
|
||||||
export default function WebServer() {
|
export default function WebServer() {
|
||||||
const [status, setStatus] = useState<{ nginx: { active: boolean }; apache: { active: boolean } } | null>(null)
|
const [status, setStatus] = useState<{ nginx: { active: boolean }; apache: { active: boolean } } | null>(null)
|
||||||
const [vhosts, setVhosts] = useState<{ domain: string; path: string }[]>([])
|
const [, setVhosts] = useState<{ domain: string; path: string }[]>([])
|
||||||
const [logs, setLogs] = useState<{ lines: string[] } | null>(null)
|
const [logs, setLogs] = useState<{ lines: string[] } | null>(null)
|
||||||
const [logType, setLogType] = useState<'access' | 'error'>('access')
|
const [logType, setLogType] = useState<'access' | 'error'>('access')
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "nixpanel-server",
|
"name": "nixpanel-server",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"server",
|
"server",
|
||||||
|
|||||||
+18
-5
@@ -28,7 +28,7 @@ cat <<'EOF'
|
|||||||
Open-Source Web Hosting Control Panel
|
Open-Source Web Hosting Control Panel
|
||||||
EOF
|
EOF
|
||||||
echo -e "${RESET}"
|
echo -e "${RESET}"
|
||||||
echo -e " Version : ${BOLD}0.1.0${RESET}"
|
echo -e " Version : ${BOLD}0.3.0${RESET}"
|
||||||
echo -e " GitHub : ${BOLD}https://github.com/NixReaper/nixpanel-server${RESET}"
|
echo -e " GitHub : ${BOLD}https://github.com/NixReaper/nixpanel-server${RESET}"
|
||||||
echo -e " NixServer: ${BOLD}https://<your-ip>:2087${RESET}"
|
echo -e " NixServer: ${BOLD}https://<your-ip>:2087${RESET}"
|
||||||
echo -e " NixClient: ${BOLD}https://<your-ip>:2083${RESET}"
|
echo -e " NixClient: ${BOLD}https://<your-ip>:2083${RESET}"
|
||||||
@@ -121,12 +121,24 @@ echo -e "${BOLD}Installation summary:${RESET}"
|
|||||||
echo " Install directory : $INSTALL_DIR"
|
echo " Install directory : $INSTALL_DIR"
|
||||||
echo " Admin username : $ADMIN_USER"
|
echo " Admin username : $ADMIN_USER"
|
||||||
echo " Admin email : $ADMIN_EMAIL"
|
echo " Admin email : $ADMIN_EMAIL"
|
||||||
echo " Admin password : ${GENERATED_PASS:+<auto-generated, shown at end>}${GENERATED_PASS:-$ADMIN_PASS}"
|
if [[ "$GENERATED_PASS" == "true" ]]; then
|
||||||
|
echo " Admin password : <auto-generated, shown at end>"
|
||||||
|
else
|
||||||
|
echo " Admin password : $ADMIN_PASS"
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
read -r -p "Continue? [Y/n] " CONFIRM
|
read -r -p "Continue? [Y/n] " CONFIRM
|
||||||
CONFIRM="${CONFIRM:-Y}"
|
CONFIRM="${CONFIRM:-Y}"
|
||||||
[[ "$CONFIRM" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; }
|
[[ "$CONFIRM" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; }
|
||||||
|
|
||||||
|
# ── Logging ───────────────────────────────────────────────────────────────────
|
||||||
|
LOG_FILE="/var/log/nixpanel-install.log"
|
||||||
|
exec > >(tee -a "$LOG_FILE") 2>&1
|
||||||
|
echo "=================================================="
|
||||||
|
echo " NixPanel Install Log — $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
||||||
|
echo " OS: ${PRETTY_NAME:-$OS_ID} Arch: $(uname -m)"
|
||||||
|
echo "=================================================="
|
||||||
|
|
||||||
# ── Update package index ──────────────────────────────────────────────────────
|
# ── Update package index ──────────────────────────────────────────────────────
|
||||||
step "Updating package index"
|
step "Updating package index"
|
||||||
if [[ "$PKG_MGR" == "apt" ]]; then
|
if [[ "$PKG_MGR" == "apt" ]]; then
|
||||||
@@ -205,7 +217,7 @@ mariadb -u root <<SQL
|
|||||||
CREATE DATABASE IF NOT EXISTS \`${NIXPANEL_DB_NAME}\`
|
CREATE DATABASE IF NOT EXISTS \`${NIXPANEL_DB_NAME}\`
|
||||||
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
CREATE USER IF NOT EXISTS '${NIXPANEL_DB_USER}'@'localhost'
|
CREATE OR REPLACE USER '${NIXPANEL_DB_USER}'@'localhost'
|
||||||
IDENTIFIED BY '${NIXPANEL_DB_PASS}';
|
IDENTIFIED BY '${NIXPANEL_DB_PASS}';
|
||||||
|
|
||||||
GRANT ALL PRIVILEGES ON \`${NIXPANEL_DB_NAME}\`.* TO '${NIXPANEL_DB_USER}'@'localhost';
|
GRANT ALL PRIVILEGES ON \`${NIXPANEL_DB_NAME}\`.* TO '${NIXPANEL_DB_USER}'@'localhost';
|
||||||
@@ -382,9 +394,9 @@ success "npm dependencies installed"
|
|||||||
step "Running database migrations"
|
step "Running database migrations"
|
||||||
|
|
||||||
cd "$INSTALL_DIR/server"
|
cd "$INSTALL_DIR/server"
|
||||||
npx prisma generate --silent
|
npx prisma generate
|
||||||
DATABASE_URL="mysql://${NIXPANEL_DB_USER}:${NIXPANEL_DB_PASS}@localhost:3306/${NIXPANEL_DB_NAME}" \
|
DATABASE_URL="mysql://${NIXPANEL_DB_USER}:${NIXPANEL_DB_PASS}@localhost:3306/${NIXPANEL_DB_NAME}" \
|
||||||
npx prisma migrate deploy
|
npx prisma db push
|
||||||
success "Database schema applied"
|
success "Database schema applied"
|
||||||
|
|
||||||
# ── Seed default data ─────────────────────────────────────────────────────────
|
# ── Seed default data ─────────────────────────────────────────────────────────
|
||||||
@@ -653,6 +665,7 @@ echo ""
|
|||||||
echo -e " ${BOLD}Files${RESET}"
|
echo -e " ${BOLD}Files${RESET}"
|
||||||
echo -e " Install : ${INSTALL_DIR}"
|
echo -e " Install : ${INSTALL_DIR}"
|
||||||
echo -e " Config : ${INSTALL_DIR}/.env"
|
echo -e " Config : ${INSTALL_DIR}/.env"
|
||||||
|
echo -e " Install log: ${LOG_FILE}"
|
||||||
echo -e " Service : systemctl status nixpanel"
|
echo -e " Service : systemctl status nixpanel"
|
||||||
echo -e " Logs : journalctl -u nixpanel -f"
|
echo -e " Logs : journalctl -u nixpanel -f"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nixpanel/server",
|
"name": "@nixpanel/server",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
+1
-1
@@ -73,7 +73,7 @@ export async function buildApp() {
|
|||||||
|
|
||||||
fastify.get('/api/health', async () => ({
|
fastify.get('/api/health', async () => ({
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
version: '0.2.0',
|
version: '0.3.0',
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ function signTokens(
|
|||||||
|
|
||||||
const refreshToken = fastify.jwt.sign(
|
const refreshToken = fastify.jwt.sign(
|
||||||
{ ...payload, type: 'refresh' },
|
{ ...payload, type: 'refresh' },
|
||||||
{ secret: config.jwt.refreshSecret, expiresIn: config.jwt.refreshExpiresIn }
|
{ expiresIn: config.jwt.refreshExpiresIn }
|
||||||
)
|
)
|
||||||
|
|
||||||
return { accessToken, refreshToken }
|
return { accessToken, refreshToken }
|
||||||
@@ -157,8 +157,7 @@ export default async function authRoutes(fastify: FastifyInstance) {
|
|||||||
let payload: JwtPayload & { type?: string }
|
let payload: JwtPayload & { type?: string }
|
||||||
try {
|
try {
|
||||||
payload = fastify.jwt.verify<JwtPayload & { type?: string }>(
|
payload = fastify.jwt.verify<JwtPayload & { type?: string }>(
|
||||||
body.data.refreshToken,
|
body.data.refreshToken
|
||||||
{ secret: config.jwt.refreshSecret }
|
|
||||||
)
|
)
|
||||||
} catch {
|
} catch {
|
||||||
return reply.code(401).send({ success: false, error: 'Invalid or expired refresh token' })
|
return reply.code(401).send({ success: false, error: 'Invalid or expired refresh token' })
|
||||||
@@ -183,7 +182,7 @@ export default async function authRoutes(fastify: FastifyInstance) {
|
|||||||
const accessToken = fastify.jwt.sign(newPayload, { expiresIn: config.jwt.expiresIn })
|
const accessToken = fastify.jwt.sign(newPayload, { expiresIn: config.jwt.expiresIn })
|
||||||
const newRefresh = fastify.jwt.sign(
|
const newRefresh = fastify.jwt.sign(
|
||||||
{ ...newPayload, type: 'refresh' },
|
{ ...newPayload, type: 'refresh' },
|
||||||
{ secret: config.jwt.refreshSecret, expiresIn: config.jwt.refreshExpiresIn }
|
{ expiresIn: config.jwt.refreshExpiresIn }
|
||||||
)
|
)
|
||||||
|
|
||||||
const expiresAt = new Date()
|
const expiresAt = new Date()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { FastifyInstance } from 'fastify'
|
import type { FastifyInstance } from 'fastify'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { requireAdmin } from '../../middleware/auth.js'
|
import { requireAdmin } from '../../middleware/auth.js'
|
||||||
import { exec, MANAGED_SERVICES as _ } from '../../core/exec.js'
|
import { exec } from '../../core/exec.js'
|
||||||
import { getServiceStatus, MANAGED_SERVICES } from '../../core/system.js'
|
import { getServiceStatus, MANAGED_SERVICES } from '../../core/system.js'
|
||||||
|
|
||||||
const ServiceActionSchema = z.object({
|
const ServiceActionSchema = z.object({
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ export interface JwtPayload {
|
|||||||
exp?: number
|
exp?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Fastify type augmentation ────────────────────────────────────────────────
|
// ─── @fastify/jwt type augmentation ───────────────────────────────────────────
|
||||||
|
|
||||||
declare module 'fastify' {
|
declare module '@fastify/jwt' {
|
||||||
interface FastifyRequest {
|
interface FastifyJWT {
|
||||||
user?: JwtPayload
|
payload: JwtPayload
|
||||||
|
user: JwtPayload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user