remove api
This commit is contained in:
		
							parent
							
								
									08b09d8197
								
							
						
					
					
						commit
						c9030b34bb
					
				| @ -1,65 +0,0 @@ | |||||||
| import { Metadata, Script } from "@/lib/types"; |  | ||||||
| import { promises as fs } from "fs"; |  | ||||||
| import { NextResponse } from "next/server"; |  | ||||||
| import path from "path"; |  | ||||||
| 
 |  | ||||||
| export const dynamic = "force-static"; |  | ||||||
| 
 |  | ||||||
| const publicJsonPath = path.join(process.cwd(), 'public/json'); |  | ||||||
| const getJsonDirectory = async () => { |  | ||||||
|   if (!(await fs.stat(publicJsonPath).catch(() => null))) { |  | ||||||
|     throw new Error(`JSON path file not found: ${publicJsonPath}`); |  | ||||||
|   } |  | ||||||
|   const jsonPath = (await fs.readFile(publicJsonPath, "utf-8")).trim(); |  | ||||||
|   return path.resolve(process.cwd(), jsonPath); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const jsonDir = await getJsonDirectory(); |  | ||||||
| const metadataFileName = "metadata.json"; |  | ||||||
| const encoding = "utf-8"; |  | ||||||
| 
 |  | ||||||
| const getMetadata = async () => { |  | ||||||
|   const filePath = path.resolve(jsonDir, metadataFileName); |  | ||||||
|   const fileContent = await fs.readFile(filePath, encoding); |  | ||||||
|   const metadata: Metadata = JSON.parse(fileContent); |  | ||||||
|   return metadata; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const getScripts = async () => { |  | ||||||
|   const filePaths = (await fs.readdir(jsonDir)) |  | ||||||
|     .filter((fileName) => fileName !== metadataFileName) |  | ||||||
|     .map((fileName) => path.resolve(jsonDir, fileName)); |  | ||||||
| 
 |  | ||||||
|   const scripts = await Promise.all( |  | ||||||
|     filePaths.map(async (filePath) => { |  | ||||||
|       const fileContent = await fs.readFile(filePath, encoding); |  | ||||||
|       const script: Script = JSON.parse(fileContent); |  | ||||||
|       return script; |  | ||||||
|     }), |  | ||||||
|   ); |  | ||||||
|   return scripts; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export async function GET() { |  | ||||||
|   try { |  | ||||||
|     const metadata = await getMetadata(); |  | ||||||
|     const scripts = await getScripts(); |  | ||||||
| 
 |  | ||||||
|     const categories = metadata.categories |  | ||||||
|       .map((category) => { |  | ||||||
|         category.scripts = scripts.filter((script) => |  | ||||||
|           script.categories.includes(category.id), |  | ||||||
|         ); |  | ||||||
|         return category; |  | ||||||
|       }) |  | ||||||
|       .sort((a, b) => a.sort_order - b.sort_order); |  | ||||||
| 
 |  | ||||||
|     return NextResponse.json(categories); |  | ||||||
|   } catch (error) { |  | ||||||
|     console.error(error as Error); |  | ||||||
|     return NextResponse.json( |  | ||||||
|       { error: "Failed to fetch categories" }, |  | ||||||
|       { status: 500 }, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -1,198 +0,0 @@ | |||||||
| "use client"; |  | ||||||
| 
 |  | ||||||
| import React, { JSX, useEffect, useState } from "react"; |  | ||||||
| import DatePicker from 'react-datepicker'; |  | ||||||
| import 'react-datepicker/dist/react-datepicker.css'; |  | ||||||
| import ApplicationChart from "../../components/ApplicationChart"; |  | ||||||
| 
 |  | ||||||
| interface DataModel { |  | ||||||
|   id: number; |  | ||||||
|   ct_type: number; |  | ||||||
|   disk_size: number; |  | ||||||
|   core_count: number; |  | ||||||
|   ram_size: number; |  | ||||||
|   os_type: string; |  | ||||||
|   os_version: string; |  | ||||||
|   disableip6: string; |  | ||||||
|   nsapp: string; |  | ||||||
|   created_at: string; |  | ||||||
|   method: string; |  | ||||||
|   pve_version: string; |  | ||||||
|   status: string; |  | ||||||
|   error: string; |  | ||||||
|   type: string; |  | ||||||
|   [key: string]: any; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| interface SummaryData { |  | ||||||
|   total_entries: number; |  | ||||||
|   status_count: Record<string, number>; |  | ||||||
|   nsapp_count: Record<string, number>; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const DataFetcher: React.FC = () => { |  | ||||||
|   const [data, setData] = useState<DataModel[]>([]); |  | ||||||
|   const [summary, setSummary] = useState<SummaryData | null>(null); |  | ||||||
|   const [loading, setLoading] = useState<boolean>(true); |  | ||||||
|   const [error, setError] = useState<string | null>(null); |  | ||||||
|   const [currentPage, setCurrentPage] = useState(1); |  | ||||||
|   const [itemsPerPage, setItemsPerPage] = useState(25); |  | ||||||
|   const [sortConfig, setSortConfig] = useState<{ key: string; direction: 'ascending' | 'descending' } | null>(null); |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     const fetchSummary = async () => { |  | ||||||
|       try { |  | ||||||
|         const response = await fetch("https://api.htl-braunau.at/data/summary"); |  | ||||||
|         if (!response.ok) throw new Error(`Failed to fetch summary: ${response.statusText}`); |  | ||||||
|         const result: SummaryData = await response.json(); |  | ||||||
|         setSummary(result); |  | ||||||
|       } catch (err) { |  | ||||||
|         setError((err as Error).message); |  | ||||||
|       } |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     fetchSummary(); |  | ||||||
|   }, []); |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     const fetchPaginatedData = async () => { |  | ||||||
|       setLoading(true); |  | ||||||
|       try { |  | ||||||
|         const response = await fetch(`https://api.htl-braunau.at/data/paginated?page=${currentPage}&limit=${itemsPerPage === 0 ? '' : itemsPerPage}`); |  | ||||||
|         if (!response.ok) throw new Error(`Failed to fetch data: ${response.statusText}`); |  | ||||||
|         const result: DataModel[] = await response.json(); |  | ||||||
|         setData(result); |  | ||||||
|       } catch (err) { |  | ||||||
|         setError((err as Error).message); |  | ||||||
|       } finally { |  | ||||||
|         setLoading(false); |  | ||||||
|       } |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     fetchPaginatedData(); |  | ||||||
|   }, [currentPage, itemsPerPage]); |  | ||||||
| 
 |  | ||||||
|   const sortedData = React.useMemo(() => { |  | ||||||
|     if (!sortConfig) return data; |  | ||||||
|     const sorted = [...data].sort((a, b) => { |  | ||||||
|       if (a[sortConfig.key] < b[sortConfig.key]) { |  | ||||||
|         return sortConfig.direction === 'ascending' ? -1 : 1; |  | ||||||
|       } |  | ||||||
|       if (a[sortConfig.key] > b[sortConfig.key]) { |  | ||||||
|         return sortConfig.direction === 'ascending' ? 1 : -1; |  | ||||||
|       } |  | ||||||
|       return 0; |  | ||||||
|     }); |  | ||||||
|     return sorted; |  | ||||||
|   }, [data, sortConfig]); |  | ||||||
| 
 |  | ||||||
|   if (loading) return <p>Loading...</p>; |  | ||||||
|   if (error) return <p>Error: {error}</p>; |  | ||||||
| 
 |  | ||||||
|   const requestSort = (key: string) => { |  | ||||||
|     let direction: 'ascending' | 'descending' = 'ascending'; |  | ||||||
|     if (sortConfig && sortConfig.key === key && sortConfig.direction === 'ascending') { |  | ||||||
|       direction = 'descending'; |  | ||||||
|     } |  | ||||||
|     setSortConfig({ key, direction }); |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   const formatDate = (dateString: string): string => { |  | ||||||
|     const date = new Date(dateString); |  | ||||||
|     const year = date.getFullYear(); |  | ||||||
|     const month = date.getMonth() + 1; |  | ||||||
|     const day = date.getDate(); |  | ||||||
|     const hours = String(date.getHours()).padStart(2, '0'); |  | ||||||
|     const minutes = String(date.getMinutes()).padStart(2, '0'); |  | ||||||
|     const timezoneOffset = dateString.slice(-6); |  | ||||||
|     return `${day}.${month}.${year} ${hours}:${minutes} ${timezoneOffset} GMT`; |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   return ( |  | ||||||
|     <div className="p-6 mt-20"> |  | ||||||
|       <h1 className="text-2xl font-bold mb-4 text-center">Created LXCs</h1> |  | ||||||
|       <ApplicationChart data={summary} /> |  | ||||||
|       <p className="text-lg font-bold mt-4"> </p> |  | ||||||
|       <div className="mb-4 flex justify-between items-center"> |  | ||||||
|         <p className="text-lg font-bold">{summary?.total_entries} results found</p> |  | ||||||
|         <p className="text-lg font">Status Legend: 🔄 installing {summary?.status_count["installing"] ?? 0} | ✔️ completed {summary?.status_count["done"] ?? 0} | ❌ failed {summary?.status_count["failed"] ?? 0} | ❓ unknown</p> |  | ||||||
|       </div>       |  | ||||||
|       <div className="overflow-x-auto"> |  | ||||||
|         <div className="overflow-y-auto lg:overflow-y-visible"> |  | ||||||
|           <table className="min-w-full table-auto border-collapse"> |  | ||||||
|             <thead> |  | ||||||
|               <tr> |  | ||||||
|                 <th className="px-4 py-2 border-b cursor-pointer" onClick={() => requestSort('status')}>Status</th> |  | ||||||
|                 <th className="px-4 py-2 border-b cursor-pointer" onClick={() => requestSort('type')}>Type</th> |  | ||||||
|                 <th className="px-4 py-2 border-b cursor-pointer" onClick={() => requestSort('nsapp')}>Application</th> |  | ||||||
|                 <th className="px-4 py-2 border-b cursor-pointer" onClick={() => requestSort('os_type')}>OS</th> |  | ||||||
|                 <th className="px-4 py-2 border-b cursor-pointer" onClick={() => requestSort('os_version')}>OS Version</th> |  | ||||||
|                 <th className="px-4 py-2 border-b cursor-pointer" onClick={() => requestSort('disk_size')}>Disk Size</th> |  | ||||||
|                 <th className="px-4 py-2 border-b cursor-pointer" onClick={() => requestSort('core_count')}>Core Count</th> |  | ||||||
|                 <th className="px-4 py-2 border-b cursor-pointer" onClick={() => requestSort('ram_size')}>RAM Size</th> |  | ||||||
|                 <th className="px-4 py-2 border-b cursor-pointer" onClick={() => requestSort('method')}>Method</th> |  | ||||||
|                 <th className="px-4 py-2 border-b cursor-pointer" onClick={() => requestSort('pve_version')}>PVE Version</th> |  | ||||||
|                 <th className="px-4 py-2 border-b cursor-pointer" onClick={() => requestSort('error')}>Error Message</th> |  | ||||||
|                 <th className="px-4 py-2 border-b cursor-pointer" onClick={() => requestSort('created_at')}>Created At</th> |  | ||||||
|               </tr> |  | ||||||
|             </thead> |  | ||||||
|             <tbody> |  | ||||||
|               {sortedData.map((item, index) => ( |  | ||||||
|                 <tr key={index}> |  | ||||||
|                   <td className="px-4 py-2 border-b"> |  | ||||||
|                     {item.status === "done" ? ( |  | ||||||
|                       "✔️" |  | ||||||
|                     ) : item.status === "failed" ? ( |  | ||||||
|                       "❌" |  | ||||||
|                     ) : item.status === "installing" ? ( |  | ||||||
|                       "🔄" |  | ||||||
|                     ) : ( |  | ||||||
|                       item.status |  | ||||||
|                     )} |  | ||||||
|                   </td> |  | ||||||
|                   <td className="px-4 py-2 border-b">{item.type === "lxc" ? ( |  | ||||||
|                     "📦" |  | ||||||
|                   ) : item.type === "vm" ? ( |  | ||||||
|                     "🖥️" |  | ||||||
|                   ) : ( |  | ||||||
|                     item.type |  | ||||||
|                   )}</td> |  | ||||||
|                   <td className="px-4 py-2 border-b">{item.nsapp}</td> |  | ||||||
|                   <td className="px-4 py-2 border-b">{item.os_type}</td> |  | ||||||
|                   <td className="px-4 py-2 border-b">{item.os_version}</td> |  | ||||||
|                   <td className="px-4 py-2 border-b">{item.disk_size}</td> |  | ||||||
|                   <td className="px-4 py-2 border-b">{item.core_count}</td> |  | ||||||
|                   <td className="px-4 py-2 border-b">{item.ram_size}</td> |  | ||||||
|                   <td className="px-4 py-2 border-b">{item.method}</td> |  | ||||||
|                   <td className="px-4 py-2 border-b">{item.pve_version}</td> |  | ||||||
|                   <td className="px-4 py-2 border-b">{item.error}</td> |  | ||||||
|                   <td className="px-4 py-2 border-b">{formatDate(item.created_at)}</td> |  | ||||||
|                 </tr> |  | ||||||
|               ))} |  | ||||||
|             </tbody> |  | ||||||
|           </table> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|       <div className="mt-4 flex justify-between items-center"> |  | ||||||
|         <button onClick={() => setCurrentPage(prev => Math.max(prev - 1, 1))} disabled={currentPage === 1} className="p-2 border">Previous</button> |  | ||||||
|         <span>Page {currentPage}</span> |  | ||||||
|         <button onClick={() => setCurrentPage(prev => prev + 1)} className="p-2 border">Next</button> |  | ||||||
|         <select |  | ||||||
|           value={itemsPerPage} |  | ||||||
|           onChange={(e) => setItemsPerPage(Number(e.target.value))} |  | ||||||
|           className="p-2 border" |  | ||||||
|         > |  | ||||||
|           <option value={10}>10</option> |  | ||||||
|           <option value={20}>20</option> |  | ||||||
|           <option value={50}>50</option> |  | ||||||
|           <option value={100}>100</option> |  | ||||||
|           <option value={250}>250</option> |  | ||||||
|           <option value={500}>500</option> |  | ||||||
|           <option value={5000}>5000</option> |  | ||||||
|         </select> |  | ||||||
|       </div> |  | ||||||
|     </div> |  | ||||||
|   ); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export default DataFetcher; |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 CanbiZ
						CanbiZ