feat(server service): added in start stop and restart from vms036

This commit is contained in:
2025-06-09 11:42:27 -05:00
parent a4dab55f7a
commit 29e0347f84
8 changed files with 400 additions and 110 deletions

View File

@@ -0,0 +1,77 @@
import { Hono } from "hono";
import { type Context, type Next } from "hono";
const app = new Hono();
// --- In-Memory Store for Rate Limits ---
// This Map will store when each user/key last accessed a rate-limited endpoint.
// Key: string (e.g., 'ip_address' or 'user_id_endpoint')
// Value: number (timestamp of last access in milliseconds)
const rateLimitStore = new Map<string, number>();
// --- Configuration ---
const FIFTEEN_MINUTES_MS = 5 * 60 * 1000; // 15 minutes in milliseconds
// --- Rate Limiting Middleware ---
export const simpleRateLimit = async (c: Context, next: Next) => {
// 1. Define a unique key for the rate limit
// For simplicity, we'll use a placeholder for user identification.
// In a real app:
// - If unauthenticated: Use c.req.header('x-forwarded-for') or c.req.ip (if configured/available)
// - If authenticated: Get user ID from c.req.user or similar after authentication middleware
const userIdentifier = c.req.header("x-forwarded-for") || "anonymous_user"; // Basic IP-like identifier
// You can also make the key specific to the route to have different limits per route
const routeKey = `${userIdentifier}:${c.req.path}`;
const now = Date.now();
const lastAccessTime = rateLimitStore.get(routeKey);
if (lastAccessTime) {
const timeElapsed = now - lastAccessTime;
if (timeElapsed < FIFTEEN_MINUTES_MS) {
// Limit exceeded
const timeRemainingMs = FIFTEEN_MINUTES_MS - timeElapsed;
const timeRemainingSeconds = Math.ceil(timeRemainingMs / 1000);
c.status(429); // HTTP 429: Too Many Requests
return c.json({
error: "Too Many Requests",
message: `Please wait ${timeRemainingSeconds} seconds before trying again.`,
retryAfter: timeRemainingSeconds, // Standard header for rate limiting clients
});
}
}
// If no previous access, or the 15 minutes have passed, allow the request
// and update the last access time.
rateLimitStore.set(routeKey, now);
// Continue to the next middleware or route handler
await next();
};
// --- Apply the Middleware to Specific Routes ---
app.get("/", (c) => {
return c.text("Welcome! This is a public endpoint.");
});
// This endpoint will be rate-limited
app.get("/privileged", simpleRateLimit, (c) => {
return c.text("You successfully accessed the privileged endpoint!");
});
// Another rate-limited endpoint
app.post("/submit-data", simpleRateLimit, async (c) => {
// In a real app, you'd process form data or JSON here
return c.text("Data submitted successfully (rate-limited).");
});
// Example of an endpoint that is NOT rate-limited
app.get("/health", (c) => {
return c.text("Server is healthy!");
});
export default app;

View File

@@ -4,7 +4,10 @@ param (
[string]$appPath,
[string]$command, # just the command like run start or what ever you have in npm.
[string]$description,
[string]$remote
[string]$remote,
[string]$server,
[string]$username,
[string]$admpass
)
# Example string to run with the parameters in it.
@@ -15,108 +18,174 @@ param (
$nssmPath = $AppPath + "\nssm.exe"
$npmPath = "C:\Program Files\nodejs\npm.cmd" # Path to npm.cmd
# Convert the plain-text password to a SecureString
$securePass = ConvertTo-SecureString $admpass -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential($username, $securePass)
if($remote -eq "true"){
# if(-not $username -or -not $admpass){
# Write-host "Missing adm account info please try again."
# exit 1
# }
$plantFunness = {
param ($service, $processType, $location)
# Call your PowerShell script inside plantFunness
& "$($location)\dist\server\scripts\services.ps1" -serviceName $service -option $processType -appPath $location
# & "$($location)\dist\server\scripts\services.ps1" -serviceName $service -option $processType -appPath $location
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Host "Error: This script must be run as Administrator."
exit 1
}
if(-not $service -or -not $processType){
Write-host "The service name or option is missing please enter one of them and try again."
exit 1
}
if ($processType -eq "start"){
write-host "Starting $($service)."
Start-Service $service
}
if ($processType -eq "stop"){
write-host "Stoping $($service)."
Stop-Service $service
}
if ($processType -eq "restart"){
write-host "Stoping $($service) to be restarted"
Stop-Service $service
Start-Sleep 3 # so we give it enough time to fully stop
write-host "Starting $($service)"
Start-Service $service
}
if ($processType -eq "prodStop"){
if(-not $location){
Write-host "The path to the app is missing please add it in and try again."
exit 1
}
& $nssmPath stop $service
write-host "Removing $($service)"
#& $nssmPath remove $serviceName confirm
sc.exe config $service start= disabled
}
if ($processType -eq "prodStart"){
if(-not $location){
Write-host "The path to the app is missing please add it in and try again."
exit 1
}
& $nssmPath start $service
write-host "Removing $($service)"
#& $nssmPath remove $serviceName confirm
sc.exe config $service start= auto
}
}
Invoke-Command -ComputerName $server -ScriptBlock $plantFunness -ArgumentList $service, $option, $appPath -Credential $credentials
}
Invoke-Command -ComputerName $server -ScriptBlock $plantFunness -ArgumentList $serviceName, $option, $appPath -Credential $credentials
} else {
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Host "Error: This script must be run as Administrator."
exit 1
}
if(-not $serviceName -or -not $option){
Write-host "The service name or option is missing please enter one of them and try again."
exit 1
}
if ($option -eq "start"){
write-host "Starting $($serviceName)."
Start-Service $serviceName
}
if ($option -eq "stop"){
write-host "Stoping $($serviceName)."
Stop-Service $serviceName
}
if ($option -eq "restart"){
write-host "Stoping $($serviceName) to be restarted"
Stop-Service $serviceName
Start-Sleep 3 # so we give it enough time to fully stop
write-host "Starting $($serviceName)"
Start-Service $serviceName
}
if ($option -eq "delete"){
if(-not $appPath){
Write-host "The path to the app is missing please add it in and try again."
exit 1
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Host "Error: This script must be run as Administrator."
exit 1
}
& $nssmPath stop $serviceName
write-host "Removing $($serviceName)"
& $nssmPath remove $serviceName confirm
}
if ($option -eq "prodStop"){
if(-not $appPath){
Write-host "The path to the app is missing please add it in and try again."
exit 1
}
& $nssmPath stop $serviceName
write-host "Removing $($serviceName)"
#& $nssmPath remove $serviceName confirm
sc.exe config $serviceName start= disabled
}
if ($option -eq "prodStart"){
if(-not $appPath){
Write-host "The path to the app is missing please add it in and try again."
exit 1
}
& $nssmPath start $serviceName
write-host "Removing $($serviceName)"
#& $nssmPath remove $serviceName confirm
sc.exe config $serviceName start= auto
}
if($option -eq "install"){
if(-not $appPath -or -not $description -or -not $command){
Write-host "Please check all parameters are passed to install the app.."
if(-not $serviceName -or -not $option){
Write-host "The service name or option is missing please enter one of them and try again."
exit 1
}
if ($option -eq "start"){
write-host "Starting $($serviceName)."
Start-Service $serviceName
}
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if(-not $service){
write-host $serviceName "is not installed we will install it now"
if ($option -eq "stop"){
write-host "Stoping $($serviceName)."
Stop-Service $serviceName
}
Write-Host "Installing $serviceName..."
& $nssmPath install $serviceName $npmPath $command
& $nssmPath set $serviceName AppDirectory $appPath
& $nssmPath set $serviceName Description $description
# Set recovery options
sc.exe failure $serviceName reset= 0 actions= restart/5000/restart/5000/restart/5000
& $nssmPath start $serviceName
}else{
write-host $serviceName "is already installed will push the updated info"
Write-Host "Updating $serviceName..."
if ($option -eq "restart"){
write-host "Stoping $($serviceName) to be restarted"
Stop-Service $serviceName
Start-Sleep 3 # so we give it enough time to fully stop
write-host "Starting $($serviceName)"
Start-Service $serviceName
}
if ($option -eq "delete"){
if(-not $appPath){
Write-host "The path to the app is missing please add it in and try again."
exit 1
}
& $nssmPath stop $serviceName
& $nssmPath set $serviceName AppDirectory $appPath
& $nssmPath set $serviceName Description $description
# Set recovery options
sc.exe failure $serviceName reset= 0 actions= restart/5000/restart/5000/restart/5000
Start-Sleep 4
write-host "Removing $($serviceName)"
& $nssmPath remove $serviceName confirm
}
if ($option -eq "prodStop"){
if(-not $appPath){
Write-host "The path to the app is missing please add it in and try again."
exit 1
}
& $nssmPath stop $serviceName
write-host "Removing $($serviceName)"
#& $nssmPath remove $serviceName confirm
sc.exe config $serviceName start= disabled
}
if ($option -eq "prodStart"){
if(-not $appPath){
Write-host "The path to the app is missing please add it in and try again."
exit 1
}
& $nssmPath start $serviceName
write-host "Removing $($serviceName)"
#& $nssmPath remove $serviceName confirm
sc.exe config $serviceName start= auto
}
if($option -eq "install"){
if(-not $appPath -or -not $description -or -not $command){
Write-host "Please check all parameters are passed to install the app.."
exit 1
}
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if(-not $service){
write-host $serviceName "is not installed we will install it now"
Write-Host "Installing $serviceName..."
& $nssmPath install $serviceName $npmPath $command
& $nssmPath set $serviceName AppDirectory $appPath
& $nssmPath set $serviceName Description $description
# Set recovery options
sc.exe failure $serviceName reset= 0 actions= restart/5000/restart/5000/restart/5000
& $nssmPath start $serviceName
}else{
write-host $serviceName "is already installed will push the updated info"
Write-Host "Updating $serviceName..."
& $nssmPath stop $serviceName
& $nssmPath set $serviceName AppDirectory $appPath
& $nssmPath set $serviceName Description $description
# Set recovery options
sc.exe failure $serviceName reset= 0 actions= restart/5000/restart/5000/restart/5000
Start-Sleep 4
& $nssmPath start $serviceName
}
}
}

View File

@@ -2,7 +2,6 @@ import { spawn } from "child_process";
import { eq } from "drizzle-orm";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { db } from "../../../../../database/dbclient.js";
import { settings } from "../../../../../database/schema/settings.js";
import { createLog } from "../../../logger/logger.js";
import { serverData } from "../../../../../database/schema/serverData.js";
import os from "os";
@@ -33,6 +32,12 @@ export const serviceControl = async (
scriptPath = `${process.env.DEVFOLDER}\\dist\\server\\scripts\\services.ps1`;
}
console.log(serverInfo[0].serverDNS);
const username = process.env.ADMUSER as string;
const password = process.env.ADMPASSWORD as string;
console.log(username, password);
const args = [
"-NoProfile",
"-ExecutionPolicy",
@@ -47,6 +52,12 @@ export const serviceControl = async (
serverInfo[0].serverLoc as string,
"-remote",
remote ?? "",
"-server",
serverInfo[0].serverDNS as string,
"-username",
username,
"-admpass",
password,
];
const scriptProcess = spawn("powershell", args);

View File

@@ -5,6 +5,7 @@ import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import hasCorrectRole from "../../../auth/middleware/roleCheck.js";
import { serviceControl } from "../../controller/server/serviceControl.js";
import { apiHit } from "../../../../globalUtils/apiHits.js";
import { simpleRateLimit } from "../../../../globalUtils/rateLimiter.js";
// Define the request body schema
const requestSchema = z.object({
@@ -21,7 +22,11 @@ app.openapi(
summary: "Starts, Stops, Restarts the server.",
method: "post",
path: "/serviceprocess",
middleware: [authMiddleware, hasCorrectRole(["systemAdmin"], "admin")],
middleware: [
authMiddleware,
hasCorrectRole(["systemAdmin"], "admin"),
simpleRateLimit,
],
request: {
body: {
@@ -34,7 +39,7 @@ app.openapi(
}),
async (c) => {
const { data, error } = await tryCatch(c.req.json());
//apiHit(c, { endpoint: `/serviceprocess`, lastBody: data });
//apiHit(c, { endpoint: "/bookin", lastBody: data });
if (error) {
return c.json({
success: false,