Add type annotations and improve script services

Enhanced type safety and documentation in several files, including adding explicit type annotations for script objects and function parameters. Improved error handling and code clarity in scriptDownloader.js, and updated autoSyncService.js to remove unnecessary cron job options. Refactored prisma.config.ts for schema configuration and updated server.js to support backup storage and improve parameter defaults.
This commit is contained in:
CanbiZ
2025-11-28 13:06:16 +01:00
parent 7fa132e09c
commit e3e4556f83
5 changed files with 54 additions and 19 deletions

View File

@@ -2,9 +2,9 @@ import 'dotenv/config'
import { defineConfig } from 'prisma/config'
export default defineConfig({
schema: 'prisma/schema.prisma',
migrate: {
migrations: 'prisma/migrations',
schema: {
kind: 'single',
filePath: 'prisma/schema.prisma',
},
studio: {
adapter: async () => {

View File

@@ -71,7 +71,10 @@ const handle = app.getRequestHandler();
* @property {ServerInfo} [server]
* @property {boolean} [isUpdate]
* @property {boolean} [isShell]
* @property {boolean} [isBackup]
* @property {string} [containerId]
* @property {string} [storage]
* @property {string} [backupStorage]
*/
class ScriptExecutionHandler {
@@ -720,7 +723,7 @@ class ScriptExecutionHandler {
* @param {ServerInfo} server
* @param {Function} [onComplete] - Optional callback when backup completes
*/
startSSHBackupExecution(ws, containerId, executionId, storage, server, onComplete = null) {
startSSHBackupExecution(ws, containerId, executionId, storage, server, onComplete = undefined) {
const sshService = getSSHExecutionService();
return new Promise((resolve, reject) => {
@@ -832,10 +835,10 @@ class ScriptExecutionHandler {
* @param {string} containerId
* @param {string} executionId
* @param {string} mode
* @param {ServerInfo|null} server
* @param {ServerInfo|undefined} server
* @param {string} [backupStorage] - Optional storage to backup to before update
*/
async startUpdateExecution(ws, containerId, executionId, mode = 'local', server = null, backupStorage = null) {
async startUpdateExecution(ws, containerId, executionId, mode = 'local', server = undefined, backupStorage = undefined) {
try {
// If backup storage is provided, run backup first
if (backupStorage && mode === 'ssh' && server) {

View File

@@ -23,6 +23,7 @@ import { Package, HardDrive, FolderOpen, LogOut, Archive } from "lucide-react";
import { api } from "~/trpc/react";
import { useAuth } from "./_components/AuthProvider";
import type { Server } from "~/types/server";
import type { ScriptCard } from "~/types/script";
export default function Home() {
const { isAuthenticated, logout } = useAuth();
@@ -101,9 +102,9 @@ export default function Home() {
if (!scriptCardsData?.success) return 0;
// Deduplicate scripts using Map by slug (same logic as ScriptsGrid.tsx)
const scriptMap = new Map<string, any>();
const scriptMap = new Map<string, ScriptCard>();
scriptCardsData.cards?.forEach((script) => {
scriptCardsData.cards?.forEach((script: ScriptCard) => {
if (script?.name && script?.slug) {
// Use slug as unique identifier, only keep first occurrence
if (!scriptMap.has(script.slug)) {
@@ -126,17 +127,12 @@ export default function Home() {
.replace(/^-+|-+$/g, "");
// First deduplicate GitHub scripts using Map by slug
interface ScriptCard {
name?: string;
slug?: string;
install_basenames?: string[];
}
const scriptMap = new Map<string, ScriptCard>();
scriptCardsData.cards?.forEach((script) => {
scriptCardsData.cards?.forEach((script: ScriptCard) => {
if (script?.name && script?.slug) {
if (!scriptMap.has(script.slug)) {
scriptMap.set(script.slug, script as ScriptCard);
scriptMap.set(script.slug, script);
}
}
});

View File

@@ -300,9 +300,6 @@ export class AutoSyncService {
console.log('Starting scheduled auto-sync...');
await this.executeAutoSync();
}, {
scheduled: true,
timezone: 'UTC'
});
console.log('Auto-sync cron job scheduled successfully');

View File

@@ -19,6 +19,8 @@ export class ScriptDownloaderService {
/**
* Validates that a directory path doesn't contain nested directories with the same name
* (e.g., prevents ct/ct or install/install)
* @param {string} dirPath - The directory path to validate
* @returns {boolean}
*/
validateDirectoryPath(dirPath) {
const normalizedPath = dirPath.replace(/\\/g, '/');
@@ -36,6 +38,9 @@ export class ScriptDownloaderService {
/**
* Validates that finalTargetDir doesn't contain nested directory names like ct/ct or install/install
* @param {string} targetDir - The base target directory
* @param {string} finalTargetDir - The final target directory to validate
* @returns {string}
*/
validateTargetDir(targetDir, finalTargetDir) {
// Check if finalTargetDir contains nested directory names
@@ -53,6 +58,11 @@ export class ScriptDownloaderService {
return finalTargetDir;
}
/**
* Ensure a directory exists, creating it if necessary
* @param {string} dirPath - The directory path to ensure exists
* @returns {Promise<void>}
*/
async ensureDirectoryExists(dirPath) {
// Validate the directory path to prevent nested directories with the same name
this.validateDirectoryPath(dirPath);
@@ -61,7 +71,7 @@ export class ScriptDownloaderService {
console.log(`[Directory Creation] Ensuring directory exists: ${dirPath}`);
await mkdir(dirPath, { recursive: true });
console.log(`[Directory Creation] Directory created/verified: ${dirPath}`);
} catch (error) {
} catch (/** @type {any} */ error) {
if (error.code !== 'EEXIST') {
console.error(`[Directory Creation] Error creating directory ${dirPath}:`, error.message);
throw error;
@@ -71,6 +81,11 @@ export class ScriptDownloaderService {
}
}
/**
* Extract repository path from GitHub URL
* @param {string} repoUrl - The GitHub repository URL
* @returns {string}
*/
extractRepoPath(repoUrl) {
const match = /github\.com\/([^\/]+)\/([^\/]+)/.exec(repoUrl);
if (!match) {
@@ -79,6 +94,13 @@ export class ScriptDownloaderService {
return `${match[1]}/${match[2]}`;
}
/**
* Download a file from GitHub
* @param {string} repoUrl - The GitHub repository URL
* @param {string} filePath - The file path within the repository
* @param {string} [branch] - The branch to download from
* @returns {Promise<string>}
*/
async downloadFileFromGitHub(repoUrl, filePath, branch = 'main') {
this.initializeConfig();
if (!repoUrl) {
@@ -88,6 +110,7 @@ export class ScriptDownloaderService {
const repoPath = this.extractRepoPath(repoUrl);
const url = `https://raw.githubusercontent.com/${repoPath}/${branch}/${filePath}`;
/** @type {Record<string, string>} */
const headers = {
'User-Agent': 'PVEScripts-Local/1.0',
};
@@ -106,6 +129,11 @@ export class ScriptDownloaderService {
return response.text();
}
/**
* Get repository URL for a script
* @param {import('~/types/script').Script} script - The script object
* @returns {string}
*/
getRepoUrlForScript(script) {
// Use repository_url from script if available, otherwise fallback to env or default
if (script.repository_url) {
@@ -115,6 +143,11 @@ export class ScriptDownloaderService {
return this.repoUrl;
}
/**
* Modify script content to use local paths
* @param {string} content - The script content
* @returns {string}
*/
modifyScriptContent(content) {
// Replace the build.func source line
const oldPattern = /source <\(curl -fsSL https:\/\/raw\.githubusercontent\.com\/community-scripts\/ProxmoxVE\/main\/misc\/build\.func\)/g;
@@ -123,9 +156,15 @@ export class ScriptDownloaderService {
return content.replace(oldPattern, newPattern);
}
/**
* Load a script by downloading its files
* @param {import('~/types/script').Script} script - The script to load
* @returns {Promise<{success: boolean, message: string, files: string[], error?: string}>}
*/
async loadScript(script) {
this.initializeConfig();
try {
/** @type {string[]} */
const files = [];
const repoUrl = this.getRepoUrlForScript(script);
const branch = process.env.REPO_BRANCH || 'main';