forked from syntaxbullet/aurorabot
feat: implement comprehensive item management system with admin UI, API, and asset handling utilities.
This commit is contained in:
102
shared/lib/assets.ts
Normal file
102
shared/lib/assets.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Asset URL Resolution Utilities
|
||||
* Provides helpers for constructing full asset URLs from item IDs or relative paths.
|
||||
* Works for both local development and production environments.
|
||||
*/
|
||||
|
||||
import { env } from "./env";
|
||||
|
||||
/**
|
||||
* Get the base URL for assets.
|
||||
* In production, this should be the public dashboard URL.
|
||||
* In development, it defaults to localhost with the configured port.
|
||||
*/
|
||||
function getAssetsBaseUrl(): string {
|
||||
// Check for explicitly configured asset URL first
|
||||
if (process.env.ASSETS_BASE_URL) {
|
||||
return process.env.ASSETS_BASE_URL.replace(/\/$/, ""); // Remove trailing slash
|
||||
}
|
||||
|
||||
// In production, construct from HOST/PORT or use a default
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
// If WEB_URL is set, use it (future-proofing)
|
||||
if (process.env.WEB_URL) {
|
||||
return process.env.WEB_URL.replace(/\/$/, "");
|
||||
}
|
||||
// Fallback: use the configured host and port
|
||||
const host = env.HOST === "0.0.0.0" ? "localhost" : env.HOST;
|
||||
return `http://${host}:${env.PORT}`;
|
||||
}
|
||||
|
||||
// Development: use localhost with the configured port
|
||||
return `http://localhost:${env.PORT}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full URL for an item's icon image.
|
||||
* @param itemId - The item's database ID
|
||||
* @returns Full URL to the item's icon image
|
||||
*
|
||||
* @example
|
||||
* getItemIconUrl(42) // => "http://localhost:3000/assets/items/42.png"
|
||||
*/
|
||||
export function getItemIconUrl(itemId: number): string {
|
||||
return `${getAssetsBaseUrl()}/assets/items/${itemId}.png`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full URL for any asset given its relative path.
|
||||
* @param relativePath - Path relative to the assets root (e.g., "items/42.png")
|
||||
* @returns Full URL to the asset
|
||||
*
|
||||
* @example
|
||||
* getAssetUrl("items/42.png") // => "http://localhost:3000/assets/items/42.png"
|
||||
*/
|
||||
export function getAssetUrl(relativePath: string): string {
|
||||
const cleanPath = relativePath.replace(/^\/+/, ""); // Remove leading slashes
|
||||
return `${getAssetsBaseUrl()}/assets/${cleanPath}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a URL is a local asset URL (relative path starting with /assets/).
|
||||
* @param url - The URL to check
|
||||
* @returns True if it's a local asset reference
|
||||
*/
|
||||
export function isLocalAssetUrl(url: string | null | undefined): boolean {
|
||||
if (!url) return false;
|
||||
return url.startsWith("/assets/") || url.startsWith("assets/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a relative asset path to a full URL.
|
||||
* If the URL is already absolute (http/https), returns it unchanged.
|
||||
* If it's a relative asset path, constructs the full URL.
|
||||
*
|
||||
* @param url - The URL to resolve (relative or absolute)
|
||||
* @returns The resolved full URL, or null if input was null/undefined
|
||||
*/
|
||||
export function resolveAssetUrl(url: string | null | undefined): string | null {
|
||||
if (!url) return null;
|
||||
|
||||
// Already absolute
|
||||
if (url.startsWith("http://") || url.startsWith("https://")) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Relative asset path
|
||||
if (isLocalAssetUrl(url)) {
|
||||
const cleanPath = url.replace(/^\/+assets\//, "").replace(/^assets\//, "");
|
||||
return getAssetUrl(cleanPath);
|
||||
}
|
||||
|
||||
// Unknown format, return as-is
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the placeholder image URL for items without an uploaded image.
|
||||
* @returns Full URL to the placeholder image
|
||||
*/
|
||||
export function getPlaceholderIconUrl(): string {
|
||||
return `${getAssetsBaseUrl()}/assets/items/placeholder.png`;
|
||||
}
|
||||
@@ -8,6 +8,9 @@ const envSchema = z.object({
|
||||
PORT: z.coerce.number().default(3000),
|
||||
HOST: z.string().default("127.0.0.1"),
|
||||
ADMIN_TOKEN: z.string().min(8, "ADMIN_TOKEN must be at least 8 characters").optional(),
|
||||
// Asset URL configuration (for production with custom domains)
|
||||
ASSETS_BASE_URL: z.string().url().optional(),
|
||||
WEB_URL: z.string().url().optional(),
|
||||
});
|
||||
|
||||
const parsedEnv = envSchema.safeParse(process.env);
|
||||
|
||||
Reference in New Issue
Block a user