From 40857fe862247da4a5b326aa28b2d1d94aa8bf67 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 13 Oct 2025 13:33:33 +0200 Subject: [PATCH 1/2] Fix: Detect downloaded scripts from all directories (ct, tools, vm, vw) - Add getAllDownloadedScripts() method to scan all script directories - Update DownloadedScriptsTab to use new API endpoint - Update ScriptsGrid to use new API endpoint for download status detection - Update main page script counts to use new API endpoint - Add recursive directory scanning to handle subdirectories - Maintain backward compatibility with existing getCtScripts endpoint Fixes issue where scripts downloaded to tools/, vm/, or vw/ directories were not showing in Downloaded Scripts tab or showing correct download status in Available Scripts tab. --- src/app/_components/DownloadedScriptsTab.tsx | 2 +- src/app/_components/ScriptsGrid.tsx | 2 +- src/app/page.tsx | 2 +- src/server/api/routers/scripts.ts | 10 +++ src/server/lib/scripts.ts | 89 ++++++++++++++++++++ 5 files changed, 102 insertions(+), 3 deletions(-) diff --git a/src/app/_components/DownloadedScriptsTab.tsx b/src/app/_components/DownloadedScriptsTab.tsx index 799d4b9..ebdabd0 100644 --- a/src/app/_components/DownloadedScriptsTab.tsx +++ b/src/app/_components/DownloadedScriptsTab.tsx @@ -37,7 +37,7 @@ export function DownloadedScriptsTab({ onInstallScript }: DownloadedScriptsTabPr const gridRef = useRef(null); const { data: scriptCardsData, isLoading: githubLoading, error: githubError, refetch } = api.scripts.getScriptCardsWithCategories.useQuery(); - const { data: localScriptsData, isLoading: localLoading, error: localError } = api.scripts.getCtScripts.useQuery(); + const { data: localScriptsData, isLoading: localLoading, error: localError } = api.scripts.getAllDownloadedScripts.useQuery(); const { data: scriptData } = api.scripts.getScriptBySlug.useQuery( { slug: selectedSlug ?? '' }, { enabled: !!selectedSlug } diff --git a/src/app/_components/ScriptsGrid.tsx b/src/app/_components/ScriptsGrid.tsx index 520fdc5..a01c266 100644 --- a/src/app/_components/ScriptsGrid.tsx +++ b/src/app/_components/ScriptsGrid.tsx @@ -34,7 +34,7 @@ export function ScriptsGrid({ onInstallScript }: ScriptsGridProps) { const gridRef = useRef(null); const { data: scriptCardsData, isLoading: githubLoading, error: githubError, refetch } = api.scripts.getScriptCardsWithCategories.useQuery(); - const { data: localScriptsData, isLoading: localLoading, error: localError } = api.scripts.getCtScripts.useQuery(); + const { data: localScriptsData, isLoading: localLoading, error: localError } = api.scripts.getAllDownloadedScripts.useQuery(); const { data: scriptData } = api.scripts.getScriptBySlug.useQuery( { slug: selectedSlug ?? '' }, { enabled: !!selectedSlug } diff --git a/src/app/page.tsx b/src/app/page.tsx index 16cce0c..db9106a 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -21,7 +21,7 @@ export default function Home() { // Fetch data for script counts const { data: scriptCardsData } = api.scripts.getScriptCardsWithCategories.useQuery(); - const { data: localScriptsData } = api.scripts.getCtScripts.useQuery(); + const { data: localScriptsData } = api.scripts.getAllDownloadedScripts.useQuery(); const { data: installedScriptsData } = api.installedScripts.getAllInstalledScripts.useQuery(); // Calculate script counts diff --git a/src/server/api/routers/scripts.ts b/src/server/api/routers/scripts.ts index fdd801f..3b27e42 100644 --- a/src/server/api/routers/scripts.ts +++ b/src/server/api/routers/scripts.ts @@ -27,6 +27,16 @@ export const scriptsRouter = createTRPCRouter({ }; }), + // Get all downloaded scripts from all directories + getAllDownloadedScripts: publicProcedure + .query(async () => { + const scripts = await scriptManager.getAllDownloadedScripts(); + return { + scripts, + directoryInfo: scriptManager.getScriptsDirectoryInfo() + }; + }), + // Get script content for viewing getScriptContent: publicProcedure diff --git a/src/server/lib/scripts.ts b/src/server/lib/scripts.ts index 1d3da37..b80bea6 100644 --- a/src/server/lib/scripts.ts +++ b/src/server/lib/scripts.ts @@ -141,6 +141,95 @@ export class ScriptManager { } } + /** + * Get all downloaded scripts from all directories (ct, tools, vm, vw) + */ + async getAllDownloadedScripts(): Promise { + this.initializeConfig(); + const allScripts: ScriptInfo[] = []; + + // Define all script directories to scan + const scriptDirs = ['ct', 'tools', 'vm', 'vw']; + + for (const dirName of scriptDirs) { + try { + const dirPath = join(this.scriptsDir!, dirName); + + // Check if directory exists + try { + await stat(dirPath); + } catch { + // Directory doesn't exist, skip it + continue; + } + + const scripts = await this.getScriptsFromDirectory(dirPath, dirName); + allScripts.push(...scripts); + } catch (error) { + console.error(`Error reading ${dirName} scripts directory:`, error); + // Continue with other directories even if one fails + } + } + + return allScripts.sort((a, b) => a.name.localeCompare(b.name)); + } + + /** + * Get scripts from a specific directory (recursively) + */ + private async getScriptsFromDirectory(dirPath: string, dirName: string): Promise { + const scripts: ScriptInfo[] = []; + + const scanDirectory = async (currentPath: string, relativePath: string = ''): Promise => { + const files = await readdir(currentPath); + + for (const file of files) { + const filePath = join(currentPath, file); + const stats = await stat(filePath); + + if (stats.isFile()) { + const extension = extname(file); + + // Check if file extension is allowed + if (this.allowedExtensions!.includes(extension)) { + // Check if file is executable + const executable = await this.isExecutable(filePath); + + // Extract slug from filename (remove .sh extension) + const slug = file.replace(/\.sh$/, ''); + + // Try to get logo from JSON data + let logo: string | undefined; + try { + const scriptData = await localScriptsService.getScriptBySlug(slug); + logo = scriptData?.logo ?? undefined; + } catch { + // JSON file might not exist, that's okay + } + + scripts.push({ + name: file, + path: filePath, + extension, + size: stats.size, + lastModified: stats.mtime, + executable, + logo, + slug + }); + } + } else if (stats.isDirectory()) { + // Recursively scan subdirectories + const subRelativePath = relativePath ? join(relativePath, file) : file; + await scanDirectory(filePath, subRelativePath); + } + } + }; + + await scanDirectory(dirPath); + return scripts; + } + /** * Check if a file is executable */ From b4e168c29489643408bdd5db0405235a502d9a7e Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 13 Oct 2025 13:36:28 +0200 Subject: [PATCH 2/2] Fix: Remove redundant type annotation and method call arguments - Remove redundant string type annotation from relativePath parameter - Fix getScriptsFromDirectory method call to match updated signature --- src/server/lib/scripts.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/lib/scripts.ts b/src/server/lib/scripts.ts index b80bea6..33038f1 100644 --- a/src/server/lib/scripts.ts +++ b/src/server/lib/scripts.ts @@ -163,7 +163,7 @@ export class ScriptManager { continue; } - const scripts = await this.getScriptsFromDirectory(dirPath, dirName); + const scripts = await this.getScriptsFromDirectory(dirPath); allScripts.push(...scripts); } catch (error) { console.error(`Error reading ${dirName} scripts directory:`, error); @@ -177,10 +177,10 @@ export class ScriptManager { /** * Get scripts from a specific directory (recursively) */ - private async getScriptsFromDirectory(dirPath: string, dirName: string): Promise { + private async getScriptsFromDirectory(dirPath: string): Promise { const scripts: ScriptInfo[] = []; - const scanDirectory = async (currentPath: string, relativePath: string = ''): Promise => { + const scanDirectory = async (currentPath: string, relativePath = ''): Promise => { const files = await readdir(currentPath); for (const file of files) {