Files
isocreator/ts/classes/iso-cache.ts

213 lines
5.1 KiB
TypeScript

/**
* ISO Cache Manager
* Manages cached Ubuntu ISOs with multi-version support
*/
import { log } from '../logging.ts';
import { path } from '../plugins.ts';
import { getCacheDir, ensureDir } from '../paths.ts';
import { IsoDownloader } from './iso-downloader.ts';
export interface ICacheEntry {
version: string;
architecture: 'amd64' | 'arm64';
filename: string;
path: string;
size: number;
downloadedAt: Date;
}
export class IsoCache {
private cacheDir: string;
private metadataPath: string;
constructor() {
this.cacheDir = getCacheDir();
this.metadataPath = path.join(this.cacheDir, 'metadata.json');
}
/**
* Initialize the cache directory
*/
async init(): Promise<void> {
await ensureDir(this.cacheDir);
}
/**
* Get all cache entries
*/
async list(): Promise<ICacheEntry[]> {
try {
const metadata = await Deno.readTextFile(this.metadataPath);
return JSON.parse(metadata);
} catch (err) {
if (err instanceof Deno.errors.NotFound) {
return [];
}
throw err;
}
}
/**
* Get a specific cache entry
*/
async get(version: string, architecture: 'amd64' | 'arm64'): Promise<ICacheEntry | null> {
const entries = await this.list();
return entries.find((e) => e.version === version && e.architecture === architecture) || null;
}
/**
* Check if an ISO is cached
*/
async has(version: string, architecture: 'amd64' | 'arm64'): Promise<boolean> {
const entry = await this.get(version, architecture);
if (!entry) return false;
// Verify the file still exists
try {
const stat = await Deno.stat(entry.path);
return stat.isFile;
} catch {
// File doesn't exist, remove from metadata
await this.remove(version, architecture);
return false;
}
}
/**
* Add an ISO to the cache
*/
async add(
version: string,
architecture: 'amd64' | 'arm64',
sourcePath: string,
): Promise<ICacheEntry> {
await this.init();
const filename = IsoDownloader.getIsoFilename(version, architecture);
const destPath = path.join(this.cacheDir, filename);
// Copy file to cache
await Deno.copyFile(sourcePath, destPath);
// Get file size
const stat = await Deno.stat(destPath);
const entry: ICacheEntry = {
version,
architecture,
filename,
path: destPath,
size: stat.size,
downloadedAt: new Date(),
};
// Update metadata
const entries = await this.list();
const existingIndex = entries.findIndex(
(e) => e.version === version && e.architecture === architecture,
);
if (existingIndex >= 0) {
entries[existingIndex] = entry;
} else {
entries.push(entry);
}
await this.saveMetadata(entries);
log.success(`ISO cached: ${version} ${architecture}`);
return entry;
}
/**
* Remove an ISO from the cache
*/
async remove(version: string, architecture: 'amd64' | 'arm64'): Promise<void> {
const entry = await this.get(version, architecture);
if (!entry) {
log.warn(`ISO not found in cache: ${version} ${architecture}`);
return;
}
// Remove file
try {
await Deno.remove(entry.path);
} catch (err) {
if (!(err instanceof Deno.errors.NotFound)) {
throw err;
}
}
// Update metadata
const entries = await this.list();
const filtered = entries.filter(
(e) => !(e.version === version && e.architecture === architecture),
);
await this.saveMetadata(filtered);
log.success(`ISO removed from cache: ${version} ${architecture}`);
}
/**
* Clean old cached ISOs
*/
async clean(olderThanDays?: number): Promise<void> {
const entries = await this.list();
const now = new Date();
for (const entry of entries) {
const age = now.getTime() - new Date(entry.downloadedAt).getTime();
const ageDays = age / (1000 * 60 * 60 * 24);
if (!olderThanDays || ageDays > olderThanDays) {
await this.remove(entry.version, entry.architecture);
}
}
}
/**
* Get the path to a cached ISO
*/
async getPath(version: string, architecture: 'amd64' | 'arm64'): Promise<string | null> {
const entry = await this.get(version, architecture);
return entry?.path || null;
}
/**
* Download and cache an ISO
*/
async downloadAndCache(
version: string,
architecture: 'amd64' | 'arm64',
onProgress?: (downloaded: number, total: number) => void,
): Promise<string> {
await this.init();
const filename = IsoDownloader.getIsoFilename(version, architecture);
const destPath = path.join(this.cacheDir, filename);
const downloader = new IsoDownloader();
await downloader.downloadWithVerification({
ubuntuVersion: version,
architecture,
outputPath: destPath,
onProgress,
});
// Add to cache metadata
await this.add(version, architecture, destPath);
return destPath;
}
/**
* Save metadata to disk
*/
private async saveMetadata(entries: ICacheEntry[]): Promise<void> {
await ensureDir(this.cacheDir);
await Deno.writeTextFile(this.metadataPath, JSON.stringify(entries, null, 2));
}
}