fix: tunnel stability, NSSM service installer, cleanup old service scripts
This commit is contained in:
1 parent
906df823b5
commit
3dcad8878b
10 files changed
+30890
-472
No files matched your search
@@ -1,37 +0,0 @@
|
||||
@echo off
|
||||
:: Check for Admin Privileges
|
||||
net session >nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo =========================================================================
|
||||
echo [ATTENTION] Administrator privileges required!
|
||||
echo Please right-click this file and select "Run as administrator".
|
||||
echo =========================================================================
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo =========================================================================
|
||||
echo Fixing Cloudflare Tunnel: Removing Conflicting Windows Background Service
|
||||
echo =========================================================================
|
||||
echo.
|
||||
|
||||
echo [1/3] Stopping existing Cloudflared service...
|
||||
net stop Cloudflared >nul 2>&1
|
||||
sc stop Cloudflared >nul 2>&1
|
||||
|
||||
echo [2/3] Uninstalling old Cloudflared Windows service...
|
||||
"%~dp0bin\cloudflared.exe" service uninstall >nul 2>&1
|
||||
sc delete Cloudflared >nul 2>&1
|
||||
|
||||
echo [3/3] Terminating any stale background cloudflared processes...
|
||||
taskkill /F /IM cloudflared.exe >nul 2>&1
|
||||
|
||||
echo.
|
||||
echo =========================================================================
|
||||
echo [SUCCESS] Conflicting service removed!
|
||||
echo The TIS RMS server / start_tunnel will now handle the tunnel cleanly
|
||||
echo without any 502 Bad Gateway or socket collisions.
|
||||
echo =========================================================================
|
||||
echo.
|
||||
pause
|
||||
@@ -0,0 +1,183 @@
|
||||
@echo off
|
||||
setlocal EnableDelayedExpansion
|
||||
:: ============================================================
|
||||
:: TIS RMS - NSSM Service Installer
|
||||
:: Installs Node.js backend as a Windows Service using NSSM.
|
||||
:: Also fixes ghost Cloudflared service and firewall rules.
|
||||
:: Run as Administrator (right-click -> Run as administrator)
|
||||
:: ============================================================
|
||||
|
||||
NET SESSION >nul 2>&1
|
||||
IF %ERRORLEVEL% NEQ 0 (
|
||||
echo [ERROR] This script must be run as Administrator.
|
||||
echo Right-click the file and choose "Run as administrator"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: ── Config ────────────────────────────────────────────────────
|
||||
set BACKEND_DIR=F:\SumbrerongBato\tis_rms_server\backend
|
||||
set NSSM=%BACKEND_DIR%\bin\nssm.exe
|
||||
set CLOUDFLARED=%BACKEND_DIR%\bin\cloudflared.exe
|
||||
set SERVICE_NAME=TIS.RMS.ServerService
|
||||
set LOG_DIR=%BACKEND_DIR%\logs
|
||||
set NODE_PORT=18484
|
||||
|
||||
echo.
|
||||
echo ============================================================
|
||||
echo TIS RMS - NSSM Service Installer
|
||||
echo ============================================================
|
||||
echo.
|
||||
|
||||
:: ── 0. Verify NSSM exists ────────────────────────────────────
|
||||
IF NOT EXIST "%NSSM%" (
|
||||
echo [ERROR] nssm.exe not found at: %NSSM%
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo [OK] NSSM found: %NSSM%
|
||||
|
||||
:: ── 1. Find node.exe ─────────────────────────────────────────
|
||||
echo.
|
||||
echo [1/7] Locating node.exe...
|
||||
set NODE_EXE=
|
||||
|
||||
:: Check bundled node first
|
||||
IF EXIST "%BACKEND_DIR%\node.exe" set NODE_EXE=%BACKEND_DIR%\node.exe
|
||||
IF NOT DEFINED NODE_EXE IF EXIST "C:\Program Files\nodejs\node.exe" set NODE_EXE=C:\Program Files\nodejs\node.exe
|
||||
IF NOT DEFINED NODE_EXE IF EXIST "C:\Program Files (x86)\nodejs\node.exe" set NODE_EXE=C:\Program Files (x86)\nodejs\node.exe
|
||||
|
||||
:: Try PATH lookup
|
||||
IF NOT DEFINED NODE_EXE (
|
||||
for /f "delims=" %%i in ('where node.exe 2^>nul') do (
|
||||
IF NOT DEFINED NODE_EXE set NODE_EXE=%%i
|
||||
)
|
||||
)
|
||||
|
||||
IF NOT DEFINED NODE_EXE (
|
||||
echo [ERROR] node.exe not found. Install Node.js from https://nodejs.org first.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo [OK] Found: %NODE_EXE%
|
||||
|
||||
:: ── 2. Remove ghost Cloudflared Windows Service ──────────────
|
||||
echo.
|
||||
echo [2/7] Checking for ghost Cloudflared Windows service...
|
||||
sc query Cloudflared >nul 2>&1
|
||||
IF %ERRORLEVEL% EQU 0 (
|
||||
echo Found! Stopping and removing...
|
||||
sc stop Cloudflared >nul 2>&1
|
||||
timeout /t 3 /nobreak >nul
|
||||
sc delete Cloudflared >nul 2>&1
|
||||
echo [OK] Ghost Cloudflared service removed.
|
||||
) ELSE (
|
||||
echo [OK] No ghost service found.
|
||||
)
|
||||
|
||||
:: ── 3. Stop and remove existing TIS RMS service (clean reinstall) ──
|
||||
echo.
|
||||
echo [3/7] Removing existing "%SERVICE_NAME%" service (if any)...
|
||||
sc query "%SERVICE_NAME%" >nul 2>&1
|
||||
IF %ERRORLEVEL% EQU 0 (
|
||||
echo Stopping existing service...
|
||||
"%NSSM%" stop "%SERVICE_NAME%" >nul 2>&1
|
||||
timeout /t 3 /nobreak >nul
|
||||
"%NSSM%" remove "%SERVICE_NAME%" confirm >nul 2>&1
|
||||
echo [OK] Old service removed.
|
||||
) ELSE (
|
||||
echo [OK] No existing service to remove.
|
||||
)
|
||||
|
||||
:: ── 4. Create logs directory ──────────────────────────────────
|
||||
echo.
|
||||
echo [4/7] Creating logs directory...
|
||||
IF NOT EXIST "%LOG_DIR%" mkdir "%LOG_DIR%"
|
||||
echo [OK] %LOG_DIR%
|
||||
|
||||
:: ── 5. Install service via NSSM ───────────────────────────────
|
||||
echo.
|
||||
echo [5/7] Installing "%SERVICE_NAME%" via NSSM...
|
||||
|
||||
"%NSSM%" install "%SERVICE_NAME%" "%NODE_EXE%"
|
||||
"%NSSM%" set "%SERVICE_NAME%" AppDirectory "%BACKEND_DIR%"
|
||||
"%NSSM%" set "%SERVICE_NAME%" AppParameters "index.js"
|
||||
|
||||
:: Logging
|
||||
"%NSSM%" set "%SERVICE_NAME%" AppStdout "%LOG_DIR%\server.log"
|
||||
"%NSSM%" set "%SERVICE_NAME%" AppStderr "%LOG_DIR%\server.log"
|
||||
"%NSSM%" set "%SERVICE_NAME%" AppRotateFiles 1
|
||||
"%NSSM%" set "%SERVICE_NAME%" AppRotateBytes 5242880
|
||||
|
||||
:: Auto-start on boot
|
||||
"%NSSM%" set "%SERVICE_NAME%" Start SERVICE_AUTO_START
|
||||
|
||||
:: Restart on crash: 5s, 10s, 30s
|
||||
"%NSSM%" set "%SERVICE_NAME%" AppExit Default Restart
|
||||
"%NSSM%" set "%SERVICE_NAME%" AppRestartDelay 5000
|
||||
|
||||
:: Display name and description
|
||||
"%NSSM%" set "%SERVICE_NAME%" DisplayName "TIS RMS Backend Server"
|
||||
"%NSSM%" set "%SERVICE_NAME%" Description "TIS Record Management System - Node.js Backend API Server with Cloudflare Tunnel"
|
||||
|
||||
:: Shutdown: send Ctrl+C then wait 10s before kill
|
||||
"%NSSM%" set "%SERVICE_NAME%" AppStopMethodSkip 0
|
||||
"%NSSM%" set "%SERVICE_NAME%" AppStopMethodConsole 5000
|
||||
"%NSSM%" set "%SERVICE_NAME%" AppStopMethodWindow 0
|
||||
"%NSSM%" set "%SERVICE_NAME%" AppStopMethodThreads 5000
|
||||
|
||||
echo [OK] Service installed.
|
||||
|
||||
:: ── 6. Configure firewall rules ───────────────────────────────
|
||||
echo.
|
||||
echo [6/7] Configuring Windows Firewall...
|
||||
|
||||
:: cloudflared.exe - all profiles
|
||||
netsh advfirewall firewall delete rule name="TIS-RMS-Cloudflared" >nul 2>&1
|
||||
netsh advfirewall firewall add rule name="TIS-RMS-Cloudflared" dir=in action=allow program="%CLOUDFLARED%" enable=yes profile=any >nul 2>&1
|
||||
netsh advfirewall firewall add rule name="TIS-RMS-Cloudflared" dir=out action=allow program="%CLOUDFLARED%" enable=yes profile=any >nul 2>&1
|
||||
|
||||
:: Node port - all profiles
|
||||
netsh advfirewall firewall delete rule name="TIS-RMS-NodeServer-Inbound" >nul 2>&1
|
||||
netsh advfirewall firewall delete rule name="TIS-RMS-NodeServer-Outbound" >nul 2>&1
|
||||
netsh advfirewall firewall add rule name="TIS-RMS-NodeServer-Inbound" dir=in action=allow protocol=TCP localport=%NODE_PORT% enable=yes profile=any >nul 2>&1
|
||||
netsh advfirewall firewall add rule name="TIS-RMS-NodeServer-Outbound" dir=out action=allow protocol=TCP localport=%NODE_PORT% enable=yes profile=any >nul 2>&1
|
||||
|
||||
:: URL ACL reservation (allows non-admin binding)
|
||||
netsh http add urlacl url=http://+:%NODE_PORT%/ user=Everyone >nul 2>&1
|
||||
|
||||
echo [OK] Firewall rules set for all network profiles (Public/Private/Domain).
|
||||
|
||||
:: ── 7. Start the service ──────────────────────────────────────
|
||||
echo.
|
||||
echo [7/7] Starting service...
|
||||
"%NSSM%" start "%SERVICE_NAME%"
|
||||
timeout /t 4 /nobreak >nul
|
||||
|
||||
:: Verify
|
||||
sc query "%SERVICE_NAME%" | findstr /i "RUNNING" >nul 2>&1
|
||||
IF %ERRORLEVEL% EQU 0 (
|
||||
echo [OK] Service is RUNNING.
|
||||
) ELSE (
|
||||
echo [WARN] Service may still be starting up. Check status in the Server Manager app.
|
||||
)
|
||||
|
||||
:: ── Done ──────────────────────────────────────────────────────
|
||||
echo.
|
||||
echo ============================================================
|
||||
echo Installation Complete!
|
||||
echo.
|
||||
echo Service Name : %SERVICE_NAME%
|
||||
echo Node.js : %NODE_EXE%
|
||||
echo Backend Dir : %BACKEND_DIR%
|
||||
echo Logs : %LOG_DIR%\server.log
|
||||
echo Port : %NODE_PORT%
|
||||
echo Auto-Start : Yes (starts on Windows boot)
|
||||
echo.
|
||||
echo The WPF Server Manager can now Start/Stop/Restart the
|
||||
echo service using the Service page.
|
||||
echo.
|
||||
echo To uninstall later, run: uninstall_service.bat
|
||||
echo ============================================================
|
||||
echo.
|
||||
pause
|
||||
+30495
File diff suppressed because it is too large.
Load diff
@@ -5,15 +5,14 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "nodemon index.js",
|
||||
"service:install": "scripts\\install_service.bat",
|
||||
"service:uninstall": "scripts\\uninstall_service.bat",
|
||||
"service:manage": "scripts\\manage_service.bat",
|
||||
"service:start": "net start TIS_RMSServer",
|
||||
"service:stop": "net stop TIS_RMSServer",
|
||||
"service:status": "sc query TIS_RMSServer",
|
||||
"tunnel": "bin\\cloudflared.exe tunnel --url http://localhost:18484"
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"service:install": "install_service.bat",
|
||||
"service:uninstall": "uninstall_service.bat",
|
||||
"service:start": "bin\\nssm.exe start TIS.RMS.ServerService",
|
||||
"service:stop": "bin\\nssm.exe stop TIS.RMS.ServerService",
|
||||
"service:restart": "bin\\nssm.exe restart TIS.RMS.ServerService",
|
||||
"service:status": "sc query TIS.RMS.ServerService"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
@echo off
|
||||
:: ============================================================================
|
||||
:: TIS_RMS Backend - Windows Service Installer (NSSM / SC)
|
||||
:: ============================================================================
|
||||
SETLOCAL EnableDelayedExpansion
|
||||
|
||||
title TIS RMS Server - Windows Service Setup
|
||||
|
||||
:: Check for Administrator Privileges
|
||||
net session >nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo [ERROR] This script requires Administrator privileges.
|
||||
echo Please right-click install_service.bat and select "Run as administrator".
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
SET SERVICE_NAME=TIS_RMSServer
|
||||
SET DISPLAY_NAME=TIS RMS Server Manager Service
|
||||
SET DESCRIPTION=Backend Node.js service for TIS RMS (School Record Management System).
|
||||
SET BACKEND_DIR=%~dp0..
|
||||
pushd "%BACKEND_DIR%"
|
||||
SET BACKEND_DIR=%CD%
|
||||
popd
|
||||
|
||||
echo ============================================================================
|
||||
echo Installing %DISPLAY_NAME% (%SERVICE_NAME%)...
|
||||
echo Directory: %BACKEND_DIR%
|
||||
echo ============================================================================
|
||||
|
||||
:: Locate Node.js executable
|
||||
FOR /F "tokens=*" %%g IN ('where node 2^>nul') DO (
|
||||
SET NODE_EXE=%%g
|
||||
GOTO :NODE_FOUND
|
||||
)
|
||||
|
||||
:NODE_FOUND
|
||||
IF "%NODE_EXE%"=="" (
|
||||
IF EXIST "C:\Program Files\nodejs\node.exe" (
|
||||
SET "NODE_EXE=C:\Program Files\nodejs\node.exe"
|
||||
) ELSE (
|
||||
echo [ERROR] Node.js not found in PATH or standard location.
|
||||
echo Please install Node.js before proceeding.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
echo [OK] Using Node.js at: %NODE_EXE%
|
||||
|
||||
:: Ensure logs directory exists
|
||||
IF NOT EXIST "%BACKEND_DIR%\logs" mkdir "%BACKEND_DIR%\logs"
|
||||
|
||||
:: Check if NSSM executable exists in backend or PATH
|
||||
SET NSSM_EXE=
|
||||
IF EXIST "%BACKEND_DIR%\bin\nssm.exe" (
|
||||
SET "NSSM_EXE=%BACKEND_DIR%\bin\nssm.exe"
|
||||
) ELSE (
|
||||
FOR /F "tokens=*" %%g IN ('where nssm 2^>nul') DO (
|
||||
SET NSSM_EXE=%%g
|
||||
GOTO :NSSM_FOUND
|
||||
)
|
||||
)
|
||||
|
||||
:NSSM_FOUND
|
||||
|
||||
IF NOT "%NSSM_EXE%"=="" (
|
||||
echo [OK] Using NSSM binary at: %NSSM_EXE%
|
||||
|
||||
:: Stop and remove existing service if present
|
||||
"%NSSM_EXE%" stop %SERVICE_NAME% >nul 2>&1
|
||||
"%NSSM_EXE%" remove %SERVICE_NAME% confirm >nul 2>&1
|
||||
|
||||
:: Install service using NSSM
|
||||
"%NSSM_EXE%" install %SERVICE_NAME% "%NODE_EXE%" "\"%BACKEND_DIR%\index.js\""
|
||||
"%NSSM_EXE%" set %SERVICE_NAME% AppDirectory "%BACKEND_DIR%"
|
||||
"%NSSM_EXE%" set %SERVICE_NAME% DisplayName "%DISPLAY_NAME%"
|
||||
"%NSSM_EXE%" set %SERVICE_NAME% Description "%DESCRIPTION%"
|
||||
"%NSSM_EXE%" set %SERVICE_NAME% Start SERVICE_AUTO_START
|
||||
"%NSSM_EXE%" set %SERVICE_NAME% AppStdout "%BACKEND_DIR%\logs\service_out.log"
|
||||
"%NSSM_EXE%" set %SERVICE_NAME% AppStderr "%BACKEND_DIR%\logs\service_err.log"
|
||||
"%NSSM_EXE%" set %SERVICE_NAME% AppRotateFiles 1
|
||||
"%NSSM_EXE%" set %SERVICE_NAME% AppRotateBytes 10485760
|
||||
|
||||
:: Start the service
|
||||
echo Starting service %SERVICE_NAME%...
|
||||
"%NSSM_EXE%" start %SERVICE_NAME%
|
||||
|
||||
) ELSE (
|
||||
echo [INFO] NSSM executable not found. Using Windows Service Control (sc.exe) fallback...
|
||||
|
||||
:: Use PowerShell to create/register Windows Service if nssm is not present
|
||||
powershell -Command "New-Service -Name '%SERVICE_NAME%' -BinaryPathName '\"%NODE_EXE%\" \"%BACKEND_DIR%\index.js\"' -DisplayName '%DISPLAY_NAME%' -Description '%DESCRIPTION%' -StartupType Automatic" >nul 2>&1
|
||||
|
||||
sc start %SERVICE_NAME% >nul 2>&1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ============================================================================
|
||||
echo [SUCCESS] %SERVICE_NAME% installed successfully!
|
||||
echo Service status:
|
||||
sc query %SERVICE_NAME% | findstr /I "STATE"
|
||||
echo ============================================================================
|
||||
echo.
|
||||
pause
|
||||
@@ -1,118 +0,0 @@
|
||||
@echo off
|
||||
:: ============================================================================
|
||||
:: TIS_RMS Backend - Interactive Windows Service Control CLI
|
||||
:: ============================================================================
|
||||
SETLOCAL EnableDelayedExpansion
|
||||
|
||||
title TIS RMS Server Manager CLI
|
||||
|
||||
SET SERVICE_NAME=TIS_RMSServer
|
||||
SET BACKEND_DIR=%~dp0..
|
||||
pushd "%BACKEND_DIR%"
|
||||
SET BACKEND_DIR=%CD%
|
||||
popd
|
||||
|
||||
:: Check for Administrator Privileges
|
||||
net session >nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo [WARNING] Running without Administrator privileges.
|
||||
echo Some actions (Install, Uninstall, Start, Stop) may fail if user isn't Admin.
|
||||
echo.
|
||||
)
|
||||
|
||||
:MENU
|
||||
cls
|
||||
echo ============================================================================
|
||||
echo TIS RMS SERVER - WINDOWS SERVICE CONTROL
|
||||
echo ============================================================================
|
||||
echo Service Name: %SERVICE_NAME%
|
||||
echo Backend Path: %BACKEND_DIR%
|
||||
echo ----------------------------------------------------------------------------
|
||||
echo Current Status:
|
||||
sc query %SERVICE_NAME% | findstr /I "STATE"
|
||||
echo ============================================================================
|
||||
echo.
|
||||
echo [1] Start Service (net start %SERVICE_NAME%)
|
||||
echo [2] Stop Service (net stop %SERVICE_NAME%)
|
||||
echo [3] Restart Service (nssm restart %SERVICE_NAME%)
|
||||
echo [4] Service Status Details (sc query %SERVICE_NAME%)
|
||||
echo [5] Install Service (Run install_service.bat)
|
||||
echo [6] Uninstall Service (Run uninstall_service.bat)
|
||||
echo [7] View Service Logs (Open stdout/stderr log files)
|
||||
echo [8] Exit
|
||||
echo.
|
||||
SET /P CHOICE="Enter your choice [1-8]: "
|
||||
|
||||
IF "%CHOICE%"=="1" GOTO START_SVC
|
||||
IF "%CHOICE%"=="2" GOTO STOP_SVC
|
||||
IF "%CHOICE%"=="3" GOTO RESTART_SVC
|
||||
IF "%CHOICE%"=="4" GOTO STATUS_SVC
|
||||
IF "%CHOICE%"=="5" GOTO INSTALL_SVC
|
||||
IF "%CHOICE%"=="6" GOTO UNINSTALL_SVC
|
||||
IF "%CHOICE%"=="7" GOTO LOGS_SVC
|
||||
IF "%CHOICE%"=="8" GOTO END
|
||||
|
||||
echo Invalid choice. Try again.
|
||||
timeout /t 2 >nul
|
||||
GOTO MENU
|
||||
|
||||
:START_SVC
|
||||
echo Starting %SERVICE_NAME%...
|
||||
net start %SERVICE_NAME%
|
||||
pause
|
||||
GOTO MENU
|
||||
|
||||
:STOP_SVC
|
||||
echo Stopping %SERVICE_NAME%...
|
||||
net stop %SERVICE_NAME%
|
||||
pause
|
||||
GOTO MENU
|
||||
|
||||
:RESTART_SVC
|
||||
echo Restarting %SERVICE_NAME%...
|
||||
net stop %SERVICE_NAME% >nul 2>&1
|
||||
net start %SERVICE_NAME%
|
||||
pause
|
||||
GOTO MENU
|
||||
|
||||
:STATUS_SVC
|
||||
cls
|
||||
echo ============================================================================
|
||||
echo Detailed Status for %SERVICE_NAME%:
|
||||
echo ============================================================================
|
||||
sc query %SERVICE_NAME%
|
||||
echo.
|
||||
pause
|
||||
GOTO MENU
|
||||
|
||||
:INSTALL_SVC
|
||||
call "%~dp0install_service.bat"
|
||||
GOTO MENU
|
||||
|
||||
:UNINSTALL_SVC
|
||||
call "%~dp0uninstall_service.bat"
|
||||
GOTO MENU
|
||||
|
||||
:LOGS_SVC
|
||||
cls
|
||||
echo ============================================================================
|
||||
echo Service Log Files:
|
||||
echo ============================================================================
|
||||
IF EXIST "%BACKEND_DIR%\logs\service_out.log" (
|
||||
echo Opening stdout log: %BACKEND_DIR%\logs\service_out.log
|
||||
start "" notepad "%BACKEND_DIR%\logs\service_out.log"
|
||||
) ELSE (
|
||||
echo [INFO] Stdout log not created yet.
|
||||
)
|
||||
|
||||
IF EXIST "%BACKEND_DIR%\logs\service_err.log" (
|
||||
echo Opening stderr log: %BACKEND_DIR%\logs\service_err.log
|
||||
start "" notepad "%BACKEND_DIR%\logs\service_err.log"
|
||||
) ELSE (
|
||||
echo [INFO] Stderr log not created yet.
|
||||
)
|
||||
pause
|
||||
GOTO MENU
|
||||
|
||||
:END
|
||||
exit /b 0
|
||||
@@ -1,55 +0,0 @@
|
||||
@echo off
|
||||
:: ============================================================================
|
||||
:: TIS_RMS Backend - Windows Service Uninstaller
|
||||
:: ============================================================================
|
||||
SETLOCAL EnableDelayedExpansion
|
||||
|
||||
title TIS RMS Server - Remove Windows Service
|
||||
|
||||
:: Check for Administrator Privileges
|
||||
net session >nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo [ERROR] This script requires Administrator privileges.
|
||||
echo Please right-click uninstall_service.bat and select "Run as administrator".
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
SET SERVICE_NAME=TIS_RMSServer
|
||||
SET BACKEND_DIR=%~dp0..
|
||||
pushd "%BACKEND_DIR%"
|
||||
SET BACKEND_DIR=%CD%
|
||||
popd
|
||||
|
||||
echo ============================================================================
|
||||
echo Uninstalling %SERVICE_NAME%...
|
||||
echo ============================================================================
|
||||
|
||||
:: Check for NSSM
|
||||
SET NSSM_EXE=
|
||||
IF EXIST "%BACKEND_DIR%\bin\nssm.exe" (
|
||||
SET "NSSM_EXE=%BACKEND_DIR%\bin\nssm.exe"
|
||||
) ELSE (
|
||||
FOR /F "tokens=*" %%g IN ('where nssm 2^>nul') DO (
|
||||
SET NSSM_EXE=%%g
|
||||
GOTO :NSSM_FOUND
|
||||
)
|
||||
)
|
||||
|
||||
:NSSM_FOUND
|
||||
|
||||
IF NOT "%NSSM_EXE%"=="" (
|
||||
"%NSSM_EXE%" stop %SERVICE_NAME% >nul 2>&1
|
||||
"%NSSM_EXE%" remove %SERVICE_NAME% confirm
|
||||
) ELSE (
|
||||
net stop %SERVICE_NAME% >nul 2>&1
|
||||
sc delete %SERVICE_NAME%
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ============================================================================
|
||||
echo [SUCCESS] Service %SERVICE_NAME% removed.
|
||||
echo ============================================================================
|
||||
echo.
|
||||
pause
|
||||
@@ -6,15 +6,24 @@ const fs = require('fs');
|
||||
const dns = require('dns');
|
||||
const https = require('https');
|
||||
|
||||
let tunnelProcess = null;
|
||||
let isStarting = false;
|
||||
let monitorTimer = null;
|
||||
let lastInternetCheck = false;
|
||||
let lastCheckedTime = null;
|
||||
let lastLogMessage = '';
|
||||
let tunnelState = 'stopped'; // 'stopped', 'starting', 'connected', 'error', 'no_internet', 'disabled'
|
||||
let consecutiveFailures = 0;
|
||||
let detectedTunnelHostname = null;
|
||||
// ─── State ─────────────────────────────────────────────────────────────────
|
||||
let tunnelProcess = null;
|
||||
let isStarting = false;
|
||||
let monitorTimer = null;
|
||||
let signalHandlersSet = false;
|
||||
|
||||
let lastInternetCheck = false;
|
||||
let lastCheckedTime = null;
|
||||
let lastLogMessage = '';
|
||||
let tunnelState = 'stopped'; // 'stopped'|'starting'|'connected'|'error'|'no_internet'|'disabled'
|
||||
let consecutiveInternetFails = 0;
|
||||
let detectedTunnelHostname = null;
|
||||
|
||||
// Backoff config
|
||||
const BACKOFF_MIN_MS = 5_000; // 5 s initial wait after a crash
|
||||
const BACKOFF_MAX_MS = 120_000; // 2 min cap
|
||||
let crashCount = 0;
|
||||
let restartTimer = null;
|
||||
|
||||
/**
|
||||
* Finds the cloudflared executable path.
|
||||
@@ -53,48 +62,63 @@ function getCloudflaredPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Robust check for internet connectivity.
|
||||
* Tests multiple DNS endpoints and falls back to HTTPS.
|
||||
*/
|
||||
function checkInternet(timeoutMs = 5000) {
|
||||
/** Robust internet check — parallel DNS + HTTPS fallback, resolves on first success. */
|
||||
function checkInternet(timeoutMs = 6000) {
|
||||
return new Promise((resolve) => {
|
||||
let isResolved = false;
|
||||
let resolved = false;
|
||||
const done = (val) => {
|
||||
if (!isResolved) {
|
||||
isResolved = true;
|
||||
clearTimeout(timer);
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
clearTimeout(globalTimer);
|
||||
resolve(val);
|
||||
}
|
||||
};
|
||||
|
||||
const timer = setTimeout(() => done(false), timeoutMs);
|
||||
|
||||
// Try DNS resolution on multiple reliable hosts in parallel
|
||||
dns.lookup('cloudflare.com', (err1) => {
|
||||
if (!err1) return done(true);
|
||||
|
||||
dns.lookup('1.1.1.1', (err2) => {
|
||||
if (!err2) return done(true);
|
||||
|
||||
// Fallback HTTPS request
|
||||
const req = https.get('https://1.1.1.1', { timeout: 3000 }, (res) => {
|
||||
res.destroy();
|
||||
done(true);
|
||||
});
|
||||
|
||||
req.on('error', () => done(false));
|
||||
req.on('timeout', () => {
|
||||
req.destroy();
|
||||
done(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
const globalTimer = setTimeout(() => done(false), timeoutMs);
|
||||
// Parallel DNS lookups
|
||||
dns.lookup('cloudflare.com', (e) => { if (!e) done(true); });
|
||||
dns.lookup('8.8.8.8', (e) => { if (!e) done(true); });
|
||||
dns.lookup('one.one.one.one', (e) => { if (!e) done(true); });
|
||||
// HTTPS fallback
|
||||
try {
|
||||
const req = https.get('https://1.1.1.1', { timeout: 4000 }, (res) => { res.destroy(); done(true); });
|
||||
req.on('error', () => {});
|
||||
req.on('timeout', () => { req.destroy(); });
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns exponential backoff delay for the next restart attempt. */
|
||||
function getBackoffMs() {
|
||||
return Math.min(BACKOFF_MIN_MS * Math.pow(2, Math.max(0, crashCount - 1)), BACKOFF_MAX_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Cloudflare Tunnel child process.
|
||||
* Schedules the next startTunnel() call with exponential backoff.
|
||||
* Safe to call multiple times — only one pending restart is ever queued.
|
||||
*/
|
||||
function scheduleRestart() {
|
||||
if (restartTimer) return;
|
||||
const isEnabled = process.env.CLOUDFLARE_TUNNEL_ENABLED !== 'false';
|
||||
if (!isEnabled) return;
|
||||
|
||||
const delay = getBackoffMs();
|
||||
console.log(`[Tunnel] Will retry in ${(delay / 1000).toFixed(0)} s (crash #${crashCount})...`);
|
||||
restartTimer = setTimeout(async () => {
|
||||
restartTimer = null;
|
||||
if (tunnelState === 'disabled' || tunnelState === 'no_internet') return;
|
||||
if (tunnelProcess || isStarting) return;
|
||||
const hasInternet = await checkInternet();
|
||||
if (hasInternet) {
|
||||
await startTunnel();
|
||||
} else {
|
||||
console.warn('[Tunnel] No internet at restart time — will retry on next monitor tick');
|
||||
tunnelState = 'no_internet';
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the cloudflared child process with crash detection and supervised restart.
|
||||
*/
|
||||
async function startTunnel() {
|
||||
if (tunnelProcess || isStarting) return;
|
||||
@@ -111,113 +135,118 @@ async function startTunnel() {
|
||||
|
||||
const DEFAULT_TOKEN = 'eyJhIjoiZWRhMWQ4ZTc1MzNjMjBiMDcyNmM0ZGU1OWE5YTMxYzgiLCJ0IjoiZjJhOGYyYmMtMWE1YS00MmNmLWJjZTUtZWMzYzAxNzY4M2IyIiwicyI6Ik5EbGtOMkZpTldNdFpEYzVNUzAwTUdFMUxXSTFNalV0WW1RNVl6VXlaV1EzTVRWaiJ9';
|
||||
const rawToken = process.env.CLOUDFLARE_TUNNEL_TOKEN || DEFAULT_TOKEN;
|
||||
const token = rawToken.replace(/[\r\n\s'"=]/g, '').trim();
|
||||
const port = process.env.PORT || 18484;
|
||||
const token = rawToken.replace(/[\r\n\s'"=]/g, '').trim();
|
||||
|
||||
// Use default auto-negotiated protocol (QUIC with HTTP/2 fallback) and --no-autoupdate
|
||||
const args = token
|
||||
? ['tunnel', '--no-autoupdate', 'run', '--token', token]
|
||||
: ['tunnel', '--no-autoupdate', '--url', `http://localhost:${port}`];
|
||||
: ['tunnel', '--no-autoupdate', '--url', `http://localhost:${process.env.PORT || 18484}`];
|
||||
|
||||
console.log(`[Tunnel] Launching Cloudflare tunnel: "${binPath}" ${token ? 'tunnel --no-autoupdate run --token [CONFIGURED]' : args.join(' ')}`);
|
||||
console.log(`[Tunnel] Launching cloudflared (crash #${crashCount})...`);
|
||||
tunnelState = 'starting';
|
||||
const startedAt = Date.now();
|
||||
|
||||
try {
|
||||
tunnelProcess = spawn(binPath, args, {
|
||||
const proc = spawn(binPath, args, {
|
||||
windowsHide: true,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
env: { ...process.env, NO_AUTOUPDATE: 'true' }
|
||||
});
|
||||
tunnelProcess = proc;
|
||||
isStarting = false;
|
||||
console.log(`[Tunnel] Process started (PID: ${proc.pid})`);
|
||||
|
||||
const pid = tunnelProcess.pid;
|
||||
console.log(`[Tunnel] Process started (PID: ${pid})`);
|
||||
const handleLog = (data) => {
|
||||
for (const line of data.toString().split('\n')) {
|
||||
const t = line.trim();
|
||||
if (!t) continue;
|
||||
lastLogMessage = t;
|
||||
const hostMatch = t.match(/"hostname":\s*"([^"]+)"/);
|
||||
if (hostMatch?.[1]) detectedTunnelHostname = hostMatch[1];
|
||||
|
||||
const handleLogStream = (data) => {
|
||||
const str = data.toString();
|
||||
const lines = str.trim().split('\n');
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) continue;
|
||||
lastLogMessage = trimmed;
|
||||
const isConn =
|
||||
t.includes('Registered tunnel connection') ||
|
||||
(t.includes('Connection') && t.includes('registered')) ||
|
||||
t.includes('Tunnel connection curve preferences') ||
|
||||
t.includes('protocol=quic') ||
|
||||
t.includes('protocol=http2');
|
||||
|
||||
// Extract configured hostname if present in ingress logs
|
||||
const hostMatch = trimmed.match(/"hostname":\s*"([^"]+)"/);
|
||||
if (hostMatch && hostMatch[1]) {
|
||||
detectedTunnelHostname = hostMatch[1];
|
||||
if (isConn && tunnelState !== 'connected') {
|
||||
tunnelState = 'connected';
|
||||
crashCount = 0; // healthy → reset backoff
|
||||
console.log('[Tunnel] ✔ Connected to Cloudflare edge network.');
|
||||
}
|
||||
|
||||
if (trimmed.includes('Registered tunnel connection') ||
|
||||
(trimmed.includes('Connection') && trimmed.includes('registered')) ||
|
||||
trimmed.includes('Tunnel connection curve preferences') ||
|
||||
trimmed.includes('protocol=quic') ||
|
||||
trimmed.includes('protocol=http2')) {
|
||||
if (tunnelState !== 'connected') {
|
||||
tunnelState = 'connected';
|
||||
console.log(`[Tunnel] Status: Connected to Cloudflare edge network.`);
|
||||
}
|
||||
consecutiveFailures = 0;
|
||||
if (t.includes('Invalid token') || t.includes('failed to authenticate')) {
|
||||
console.error('[Tunnel] ✖ Auth failure — check CLOUDFLARE_TUNNEL_TOKEN');
|
||||
tunnelState = 'error';
|
||||
}
|
||||
}
|
||||
};
|
||||
proc.stdout.on('data', handleLog);
|
||||
proc.stderr.on('data', handleLog);
|
||||
|
||||
tunnelProcess.stdout.on('data', handleLogStream);
|
||||
tunnelProcess.stderr.on('data', handleLogStream);
|
||||
|
||||
tunnelProcess.on('error', (err) => {
|
||||
proc.on('error', (err) => {
|
||||
console.error('[Tunnel] Process error:', err.message);
|
||||
tunnelState = 'error';
|
||||
tunnelState = 'error';
|
||||
lastLogMessage = err.message;
|
||||
tunnelProcess = null;
|
||||
isStarting = false;
|
||||
if (tunnelProcess === proc) tunnelProcess = null;
|
||||
isStarting = false;
|
||||
crashCount++;
|
||||
scheduleRestart();
|
||||
});
|
||||
|
||||
tunnelProcess.on('exit', (code, signal) => {
|
||||
proc.on('exit', (code, signal) => {
|
||||
console.log(`[Tunnel] Process exited (Code: ${code}, Signal: ${signal})`);
|
||||
tunnelProcess = null;
|
||||
if (tunnelProcess === proc) tunnelProcess = null;
|
||||
isStarting = false;
|
||||
if (tunnelState !== 'no_internet' && tunnelState !== 'disabled') {
|
||||
tunnelState = 'stopped';
|
||||
|
||||
// Detect crash: process lived < 30 s
|
||||
const uptimeMs = Date.now() - startedAt;
|
||||
if (uptimeMs < 30_000) {
|
||||
crashCount++;
|
||||
console.warn(`[Tunnel] Crash detected (uptime ${(uptimeMs / 1000).toFixed(1)} s, crash #${crashCount})`);
|
||||
} else {
|
||||
crashCount = 0; // lived long enough → reset
|
||||
}
|
||||
|
||||
if (tunnelState === 'disabled' || tunnelState === 'no_internet') return;
|
||||
tunnelState = 'stopped';
|
||||
scheduleRestart();
|
||||
});
|
||||
|
||||
// If process stays alive for 3 seconds, mark as connected
|
||||
// Infer connected if still alive after 5 s with no explicit log confirmation
|
||||
setTimeout(() => {
|
||||
if (tunnelProcess && tunnelState === 'starting') {
|
||||
if (tunnelProcess === proc && tunnelState === 'starting') {
|
||||
tunnelState = 'connected';
|
||||
console.log(`[Tunnel] Status: Connected to Cloudflare edge network.`);
|
||||
consecutiveFailures = 0;
|
||||
crashCount = 0;
|
||||
console.log('[Tunnel] ✔ Connected (inferred — process alive after 5 s).');
|
||||
}
|
||||
}, 3000);
|
||||
}, 5000);
|
||||
|
||||
} catch (err) {
|
||||
console.error('[Tunnel] Failed to start tunnel process:', err.message);
|
||||
tunnelState = 'error';
|
||||
console.error('[Tunnel] Failed to spawn process:', err.message);
|
||||
tunnelState = 'error';
|
||||
lastLogMessage = err.message;
|
||||
tunnelProcess = null;
|
||||
} finally {
|
||||
isStarting = false;
|
||||
isStarting = false;
|
||||
crashCount++;
|
||||
scheduleRestart();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the Cloudflare Tunnel child process cleanly and non-blockingly.
|
||||
*/
|
||||
/** Kills the cloudflared child process cleanly and clears any pending restart timer. */
|
||||
function stopTunnel() {
|
||||
if (restartTimer) { clearTimeout(restartTimer); restartTimer = null; }
|
||||
if (!tunnelProcess) return;
|
||||
const proc = tunnelProcess;
|
||||
tunnelProcess = null;
|
||||
tunnelState = 'stopped';
|
||||
|
||||
tunnelState = 'stopped';
|
||||
try {
|
||||
console.log(`[Tunnel] Stopping tunnel process (PID: ${proc.pid})...`);
|
||||
if (process.platform === 'win32') {
|
||||
const killer = spawn('taskkill', ['/pid', String(proc.pid), '/T', '/F'], {
|
||||
windowsHide: true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
killer.on('error', () => {
|
||||
try { proc.kill('SIGTERM'); } catch (_) {}
|
||||
windowsHide: true, stdio: 'ignore'
|
||||
});
|
||||
killer.on('error', () => { try { proc.kill('SIGTERM'); } catch (_) {} });
|
||||
} else {
|
||||
proc.kill('SIGTERM');
|
||||
}
|
||||
@@ -226,9 +255,7 @@ function stopTunnel() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Periodic check loop: checks internet and maintains tunnel state resiliently.
|
||||
*/
|
||||
/** Internet watchdog — restarts tunnel if net comes back, suspends after persistent outage. */
|
||||
async function checkAndMaintainTunnel() {
|
||||
const isEnabled = process.env.CLOUDFLARE_TUNNEL_ENABLED !== 'false';
|
||||
if (!isEnabled) {
|
||||
@@ -237,79 +264,77 @@ async function checkAndMaintainTunnel() {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasInternet = await checkInternet(5000);
|
||||
const hasInternet = await checkInternet(6000);
|
||||
lastInternetCheck = hasInternet;
|
||||
lastCheckedTime = new Date().toISOString();
|
||||
lastCheckedTime = new Date().toISOString();
|
||||
|
||||
if (hasInternet) {
|
||||
consecutiveFailures = 0;
|
||||
if (!tunnelProcess && !isStarting) {
|
||||
console.log('[Tunnel] Internet connection available. Starting Cloudflare tunnel...');
|
||||
consecutiveInternetFails = 0;
|
||||
// Only start if nothing is running or pending
|
||||
if (!tunnelProcess && !isStarting && !restartTimer) {
|
||||
if (tunnelState === 'no_internet') crashCount = 0; // reset backoff after net recovery
|
||||
console.log('[Tunnel] Internet available — starting tunnel...');
|
||||
await startTunnel();
|
||||
}
|
||||
} else {
|
||||
consecutiveFailures++;
|
||||
console.warn(`[Tunnel] Internet check failed (${consecutiveFailures}/3)`);
|
||||
|
||||
// Only suspend tunnel after 3 consecutive failures (approx 90s)
|
||||
if (consecutiveFailures >= 3 && tunnelProcess) {
|
||||
console.warn('[Tunnel] Persistent internet loss. Suspending tunnel...');
|
||||
consecutiveInternetFails++;
|
||||
console.warn(`[Tunnel] Internet check failed (${consecutiveInternetFails}/3)`);
|
||||
if (consecutiveInternetFails >= 3 && tunnelProcess) {
|
||||
console.warn('[Tunnel] Persistent internet loss — suspending tunnel');
|
||||
tunnelState = 'no_internet';
|
||||
stopTunnel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes auto tunnel activation on server startup.
|
||||
*/
|
||||
/** Initializes the supervised tunnel service on server startup. */
|
||||
function startAutoTunnel() {
|
||||
const isEnabled = process.env.CLOUDFLARE_TUNNEL_ENABLED !== 'false';
|
||||
if (!isEnabled) {
|
||||
console.log('[Tunnel] Auto tunnel is disabled via CLOUDFLARE_TUNNEL_ENABLED=false');
|
||||
console.log('[Tunnel] Auto tunnel disabled (CLOUDFLARE_TUNNEL_ENABLED=false)');
|
||||
tunnelState = 'disabled';
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[Tunnel] Initializing Auto Cloudflare Tunnel Service...');
|
||||
|
||||
// Initial check immediately
|
||||
console.log('[Tunnel] Initializing supervised Cloudflare Tunnel service...');
|
||||
checkAndMaintainTunnel();
|
||||
|
||||
// Periodic check interval (default: 30 seconds to prevent aggressive CPU/DNS churn)
|
||||
const intervalMs = parseInt(process.env.CLOUDFLARE_TUNNEL_CHECK_INTERVAL, 10) || 30000;
|
||||
const intervalMs = parseInt(process.env.CLOUDFLARE_TUNNEL_CHECK_INTERVAL, 10) || 30_000;
|
||||
if (monitorTimer) clearInterval(monitorTimer);
|
||||
monitorTimer = setInterval(checkAndMaintainTunnel, intervalMs);
|
||||
|
||||
// Ensure clean shutdown on parent process exit
|
||||
const cleanExit = () => {
|
||||
if (monitorTimer) clearInterval(monitorTimer);
|
||||
stopTunnel();
|
||||
};
|
||||
|
||||
process.on('exit', cleanExit);
|
||||
process.on('SIGINT', () => { cleanExit(); process.exit(0); });
|
||||
process.on('SIGTERM', () => { cleanExit(); process.exit(0); });
|
||||
process.on('beforeExit', cleanExit);
|
||||
// Register signal handlers exactly once to avoid duplicate-listener warnings
|
||||
if (!signalHandlersSet) {
|
||||
signalHandlersSet = true;
|
||||
const cleanExit = () => {
|
||||
if (monitorTimer) { clearInterval(monitorTimer); monitorTimer = null; }
|
||||
if (restartTimer) { clearTimeout(restartTimer); restartTimer = null; }
|
||||
stopTunnel();
|
||||
};
|
||||
process.on('exit', cleanExit);
|
||||
process.on('SIGINT', () => { cleanExit(); process.exit(0); });
|
||||
process.on('SIGTERM', () => { cleanExit(); process.exit(0); });
|
||||
process.on('beforeExit', cleanExit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current tunnel status.
|
||||
*/
|
||||
/** Returns current tunnel status including crash diagnostics. */
|
||||
function getTunnelStatus() {
|
||||
const binPath = getCloudflaredPath();
|
||||
return {
|
||||
enabled: process.env.CLOUDFLARE_TUNNEL_ENABLED !== 'false',
|
||||
hasExecutable: !!binPath,
|
||||
enabled: process.env.CLOUDFLARE_TUNNEL_ENABLED !== 'false',
|
||||
hasExecutable: !!binPath,
|
||||
executablePath: binPath,
|
||||
hasInternet: lastInternetCheck,
|
||||
isRunning: !!tunnelProcess,
|
||||
pid: tunnelProcess ? tunnelProcess.pid : null,
|
||||
status: tunnelState,
|
||||
hostname: detectedTunnelHostname,
|
||||
publicUrl: detectedTunnelHostname ? `https://${detectedTunnelHostname}` : null,
|
||||
lastChecked: lastCheckedTime,
|
||||
lastLog: lastLogMessage
|
||||
hasInternet: lastInternetCheck,
|
||||
isRunning: !!tunnelProcess,
|
||||
pid: tunnelProcess ? tunnelProcess.pid : null,
|
||||
status: tunnelState,
|
||||
hostname: detectedTunnelHostname,
|
||||
publicUrl: detectedTunnelHostname ? `https://${detectedTunnelHostname}` : null,
|
||||
lastChecked: lastCheckedTime,
|
||||
lastLog: lastLogMessage,
|
||||
crashCount,
|
||||
nextRetryMs: restartTimer ? getBackoffMs() : null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
@echo off
|
||||
:: NOTE: This script is kept as an emergency fallback only.
|
||||
:: The Cloudflare Tunnel is now managed automatically by Node.js (tunnelService.js).
|
||||
:: Use install_service.bat + WPF Server Manager instead of running this manually.
|
||||
::
|
||||
title TIS RMS Cloudflare Tunnel
|
||||
echo =========================================================
|
||||
echo Starting TIS RMS Cloudflare Tunnel on port 18484
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
@echo off
|
||||
:: ============================================================
|
||||
:: TIS RMS - NSSM Service Uninstaller
|
||||
:: Run as Administrator
|
||||
:: ============================================================
|
||||
|
||||
NET SESSION >nul 2>&1
|
||||
IF %ERRORLEVEL% NEQ 0 (
|
||||
echo [ERROR] Run as Administrator.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
set BACKEND_DIR=F:\SumbrerongBato\tis_rms_server\backend
|
||||
set NSSM=%BACKEND_DIR%\bin\nssm.exe
|
||||
set SERVICE_NAME=TIS.RMS.ServerService
|
||||
|
||||
echo.
|
||||
echo Stopping and removing "%SERVICE_NAME%"...
|
||||
"%NSSM%" stop "%SERVICE_NAME%" >nul 2>&1
|
||||
timeout /t 3 /nobreak >nul
|
||||
"%NSSM%" remove "%SERVICE_NAME%" confirm
|
||||
|
||||
echo.
|
||||
echo [OK] Service removed. Node.js will no longer start on boot.
|
||||
echo You can re-install anytime by running install_service.bat
|
||||
echo.
|
||||
pause
|
||||
Reference in new issue
Block a user